当前位置 博文首页 > nameofcsdn的博客:高维DP

    nameofcsdn的博客:高维DP

    作者:[db:作者] 时间:2021-07-12 10:04

    目录

    一,2维DP

    力扣?63. 不同路径 II (空间压缩)

    力扣 64. 最小路径和(空间压缩)

    力扣?887. 鸡蛋掉落

    CSU 1899: Yuelu Scenes(空间压缩)

    UVA 12034 Race

    力扣?1314. 矩阵区域和

    二,3维DP

    力扣 1223. 掷骰子模拟(空间压缩,解空间平移)

    CSU - 1750 切蛋糕

    力扣?1420. 生成数组

    力扣 1473. 给房子涂色 III

    力扣 1504. 统计全 1 子矩形(空间压缩)


    ?

    一,2维DP

    力扣?63. 不同路径 II (空间压缩)

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

    机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

    现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

    网格中的障碍物和空位置分别用 1 和 0 来表示。

    说明:m?和 n 的值均不超过 100。

    示例?1:

    输入:
    [
    ??[0,0,0],
    ??[0,1,0],
    ??[0,0,0]
    ]
    输出: 2
    解释:
    3x3 网格的正中间有一个障碍物。
    从左上角到右下角一共有 2 条不同的路径:
    1. 向右 -> 向右 -> 向下 -> 向下
    2. 向下 -> 向下 -> 向右 -> 向右

    ?

    我的递归写法:

    class Solution {
    public:
        vector<vector<int>>ans;
        int dp(vector<vector<int>>& obs,int x,int y)
        {
            if(x<0||x>=obs.size()||y<0||y>=obs[0].size()||obs[x][y])return 0;
            if(ans[x][y])return ans[x][y];
            return ans[x][y]=dp(obs,x,y-1)+dp(obs,x-1,y);
        }
        int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
            ans=obstacleGrid;
            for(int i=0;i<ans.size();i++)for(int j=0;j<ans[0].size();j++)ans[i][j]=0;
            ans[0][0]=1;
            return dp(obstacleGrid,obstacleGrid.size()-1,obstacleGrid[0].size()-1);
        }
    };

    我的非递归写法:

    class Solution {
    public:
        vector<vector<int>>ans;
        int uniquePathsWithObstacles(vector<vector<int>>& obs) {
            vector<vector<int>> ans=obs;
            ans[0][0]=(obs[0][0]?0:1);        
            for(int j=1;j<obs[0].size();j++)ans[0][j]=(obs[0][j]?0:ans[0][j-1]);
            for(int i=1;i<obs.size();i++){
                ans[i][0]=(obs[i][0]?0:ans[i-1][0]);
                for(int j=1;j<obs[0].size();j++)ans[i][j]=(obs[i][j]?0:ans[i][j-1]+ans[i-1][j]);
            }
            return ans[ans.size()-1][ans[0].size()-1];
        }
    };

    我的空间压缩写法:

    class Solution {
    public:
        vector<vector<int>>ans;
        int uniquePathsWithObstacles(vector<vector<int>>& obs) {
            vector<int> ans=obs[0];
            ans[0]=(obs[0][0]?0:1);        
            for(int j=1;j<obs[0].size();j++)ans[j]=(obs[0][j]?0:ans[j-1]);
            for(int i=1;i<obs.size();i++){
                ans[0]=(obs[i][0]?0:ans[0]);
                for(int j=1;j<obs[0].size();j++)ans[j]=(obs[i][j]?0:ans[j-1]+ans[j]);
            }
            return ans[ans.size()-1];
        }
    };

    力扣 64. 最小路径和(空间压缩)

    题目:

    给定一个包含非负整数的 m?x?n?网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

    说明:每次只能向下或者向右移动一步。

    示例:

    输入:
    [
    ??[1,3,1],
    ? [1,5,1],
    ? [4,2,1]
    ]
    输出: 7
    解释: 因为路径 1→3→1→1→1 的总和最小。

    ?

    代码:

    class Solution {
    public:
    	int minPathSum(vector<vector<int>>& grid) {
    		if (grid.empty())return -1;
    		vector<int>ans1 = grid[0];
    		for (int i = 1; i < ans1.size(); i++)
    		{
    			ans1[i] += ans1[i - 1];
    		}
    		vector<int>ans2;
    		for (int i = 1; i < grid.size(); i++)
    		{
    			ans2 = grid[i];
    			ans1[0] += ans2[0];
    			for (int i = 1; i < ans1.size(); i++)
    			{
    				ans1[i] = min(ans1[i], ans1[i - 1]) + ans2[i];
    			}
    		}
    		return ans1[ans1.size() - 1];
    	}
    };

    力扣?887. 鸡蛋掉落

    给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。

    已知存在楼层 f ,满足?0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。

    每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足?1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。

    请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?

    ?
    示例 1:

    输入:k = 1, n = 2
    输出:2
    解释:
    鸡蛋从 1 楼掉落。如果它碎了,肯定能得出 f = 0 。?
    否则,鸡蛋从 2 楼掉落。如果它碎了,肯定能得出 f = 1 。?
    如果它没碎,那么肯定能得出 f = 2 。?
    因此,在最坏的情况下我们需要移动 2 次以确定 f 是多少。?
    示例 2:

    输入:k = 2, n = 6
    输出:3
    示例 3:

    输入:k = 3, n = 14
    输出:4
    ?

    提示:

    1 <= k <= 100
    1 <= n <= 104

    这个题目确实很经典,我高中的时候做过k=2的情况,那时候不太会编程,也只能思考k=3的情况

    我一直记得这个题目,我当时就是纯口算算出来,k=2,n=100时,答案是14

    代码:

    class Solution {
    public:
        int ans[101][10001];
        Solution(){
            for(int i=0;i<101;i++)for(int j=0;j<10001;j++)ans[i][j]=0;
        }
        int superEggDrop(int k, int n) {
            if(ans[k][n])return ans[k][n];
            if(k==1)return n;
            if(n==0)return 0;
            ans[k][n]=n;
            int low=1,high=n;
            while(high-low>5){
                int m=(low+high)/2;
                if(superEggDrop(k-1,m-1)>superEggDrop(k,n-m))high=m;
                else low=m;
            }
            for(int i=low;i<=high;i++)ans[k][n]=min(ans[k][n],1+max(superEggDrop(k-1,i-1),superEggDrop(k,n-i)));
            return ans[k][n];
        }
    };

    PS:

    需要用二分才能降低时间复杂度,而且不能用三分

    CSU 1899: Yuelu Scenes(空间压缩)

    题目:

    Description
    An artist begins with a roll of ribbon, one inch wide. She clips it into pieces of various integral lengths, then aligns them with the bottom of a frame, rising vertically in columns, to form a Yuelu scene. A Yuelu scene must be uneven; if all columns are the same height, it’s a plain scene, not a Yuelu scene! It is possible that she may not use all of the ribbon.

    If our artist has 4 inches of ribbon and a 2 × 2 inch frame, she could form these scenes:

    She would not form these scenes, because they're plains, not mountains!

    Given the length of the ribbon and the width and height of the frame, all in inches, how many different Yuelu scenes can she create? Two scenes are different if the regions covered by ribbon are different. There’s no point in putting more than one piece of ribbon in any column.

    Input
    Each input will consist of several case. Each input will consist of a single line with three space-separated integers n, w and h, where n (0 n ≤ 10,000) is the length of the ribbon in inches, w (1 ≤ w ≤ 100) is the width and h (1 ≤ h ≤ 100) is the height of the frame, both in inches.

    Output
    Output a single integer, indicating the total number of Yuelu scenes our artist could possibly make, modulo 1e9 + 7.

    Sample Input
    25 5 5
    15 5 5
    10 10 1
    4 2 2
    Sample Output
    7770
    6050
    1022
    6

    题目意思是说,在宽w高h的矩形里面放格子,格子数量不超过n,问有多少种放法使得不是每一列都完全一样高。

    其实只要求出所有的放法,再减去一样高的放法就可以了。

    考虑在宽i(i从1到w)高h的矩形格子里面放j个格子有多少种方法,用ans[i][j]表示

    那么ans[i][j]=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-h]

    数组越界的按0算,当然,编程要保证不会越界。

    这样,ans[w][0]+ans[w][1]+......+ans[w][n]就是所有的放法,再减去一样高的放法就可以了。

    用了动态规划的空间压缩(刷表),所以代码不太好看懂。

    代码:

    #include<iostream>
    using namespace std;
     
    int ans[10001],p=1000000007;
     
    int main()
    {
    	int n,w,h;
    	while(cin>>n>>w>>h)
    	{	
    		if(n>w*h)n=w*h;
    		for(int i=0;i<=n;i++)ans[i]=(i<=h);
    		for(int k=2;k<=w;k++)
    		{
    			for(int i=k*h;i>=h+1;i--)ans[i]=(ans[i]-ans[i-h-1])%p;
    			for(int i=1;i<=k*h;i++)ans[i]=(ans[i]+ans[i-1])%p;	
    		}
    		int s=-1-n/w;
    		for(int i=0;i<=n;i++)s=(s+ans[i])%p;
    		cout<<(s%p+p)%p<<endl;
    	}
    	return 0;
    }

    UVA 12034 Race

    题目:

    Disky and Sooma, two of the biggest mega minds of Bangladesh went to a far country. They ate, coded and wandered around, even in their holidays. They passed several months in this way. But everything has an end. A holy person, Munsiji came into their life. Munsiji took them to derby (horse racing). Munsiji enjoyed the race, but as usual Disky and Sooma did their as usual task instead of passing some romantic moments. They were thinking- in how many ways a race can ?nish! Who knows, maybe this is their romance! In a race there are n horses.?

    You have to output the number of ways the race can ?nish. Note that, more than one horse may get the same position. For example, 2 horses can ?nish in 3 ways.

    1. Both ?rst
    2. horse1 ?rst and horse2 second
    3. horse2 ?rst and horse1 second


    Input?

    Input starts with an integer T (≤ 1000), denoting the number of test cases. Each case starts with a line containing an integer n (1 ≤ n ≤ 1000).


    Output
    For each case, print the case number and the number of ways the race can ?nish. The result can be very large, print the result modulo 10056.


    Sample Input
    3 1 2 3


    Sample Output
    Case 1: 1?

    Case 2: 3?

    Case 3: 13
    这个题目是动态规划。

    假设有 i 个不同的球,要放在 j 个不同的盒子里面,每个盒子里面都要有球,一共有 list[i][j] 种放法

    那么有递归式list[i][j] = (list[i - 1][j - 1] + list[i - 1][j])*j

    而要求的是∑(j) list[i][j]

    代码:
    ?

    #include<iostream>
    #include<string.h>
    using namespace std;
     
    int list[1001][1001];
    int sum[1001];
     
    int main()
    {
    	
    	memset(list, 0, sizeof(list));
    	memset(sum, 0, sizeof(sum));
    	list[1][1] = 1;
    	sum[1] = 1;
    	for (int i = 2; i <= 1000; i++)for (int j = 1; j <= i; j++)
    	{
    		list[i][j] = (list[i - 1][j - 1] + list[i - 1][j])*j % 10056;
    		sum[i] += list[i][j];
    	}
    	int t, n;
    	cin >> t;
    	for (int i = 1; i <= t; i++)
    	{
    		cin >> n;
    		cout << "Case " << i << ": " << sum[n] % 10056 << endl;
    	}
    	return 0;
    }

    力扣?1314. 矩阵区域和

    ?

    给你一个?m * n?的矩阵?mat?和一个整数?K ,请你返回一个矩阵?answer?,其中每个?answer[i][j]?是所有满足下述条件的元素?mat[r][c] 的和:?

    i - K <= r <= i + K, j - K <= c <= j + K?
    (r, c)?在矩阵内。
    ?

    示例 1:

    输入:mat = [[1,2,3],[4,5,6],[7,8,9]], K = 1
    输出:[[12,21,16],[27,45,33],[24,39,28]]
    示例 2:

    输入:mat = [[1,2,3],[4,5,6],[7,8,9]], K = 2
    输出:[[45,45,45],[45,45,45],[45,45,45]]
    ?

    提示:

    m ==?mat.length
    n ==?mat[i].length
    1 <= m, n, K <= 100
    1 <= mat[i][j] <= 100

    class Solution {
    public:
        int f(vector<vector<int>> &mat, int i, int j)
        {
            if (i < 0 || j < 0) return 0;
            if (i >= mat.size()) i = mat.size() - 1;
            if (j >= mat[0].size()) j = mat[0].size() - 1;
            return mat[i][j];
        }
        vector<vector<int>> matrixBlockSum(vector<vector<int>> &mat, int K)
        {
            vector<vector<int>> s = mat, ans =mat;
            for (int i = 0; i < s.size(); i++) for (int j = 0; j < s[0].size(); j++) 
                s[i][j] = f(s, i - 1, j) + f(s, i, j - 1) - f(s, i - 1, j - 1) + mat[i][j];
            for (int i = 0; i < s.size(); i++) for (int j = 0; j < s[0].size(); j++)
                ans[i][j] = f(s, i + K, j + K) - f(s, i - K - 1, j + K) - f(s, i + K, j - K - 1) + f(s, i - K - 1, j - K - 1);
            return ans;
        }
    };

    二,3维DP

    力扣 1223. 掷骰子模拟(空间压缩,解空间平移)

    题目:

    有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。

    不过我们在使用它时有个约束,就是使得投掷骰子时,连续?掷出数字?i?的次数不能超过?rollMax[i]i?从 1 开始编号)。

    现在,给你一个整数数组?rollMax?和一个整数?n,请你来计算掷?n?次骰子可得到的不同点数序列的数量。

    假如两个序列中至少存在一个元素不同,就认为这两个序列是不同的。由于答案可能很大,所以请返回?模?10^9 + 7?之后的结果。

    ?

    示例 1:

    输入:n = 2, rollMax = [1,1,2,2,2,3]
    输出:34
    解释:我们掷 2 次骰子,如果没有约束的话,共有 6 * 6 = 36 种可能的组合。但是根据 rollMax 数组,数字 1 和 2 最多连续出现一次,所以不会出现序列 (1,1) 和 (2,2)。因此,最终答案是 36-2 = 34。

    示例 2:

    输入:n = 2, rollMax = [1,1,1,1,1,1]
    输出:30

    示例 3:

    输入:n = 3, rollMax = [1,1,1,2,2,3]
    输出:181

    ?

    提示:

    • 1 <= n <= 5000
    • rollMax.length == 6
    • 1 <= rollMax[i] <= 15

    ?

    思路一:三维DP

    用ans[i][j][k]表示,第i次掷骰子得到的数为j,且这是连续出现的第k个j的情况数

    用三维递推式算出来,最后加一加就得到答案了。

    比较简单,我就不写了。

    时间复杂度:n * rollMax.length * rollMax.getMax()? 空间复杂度:n * rollMax.length * rollMax.getMax()

    这里时间空间都是5000*6*15, 可以接受。

    ?

    思路二:空间压缩

    以计算顺序,即时间,代替空间中的一个维度。

    ans[j][k]表示当次掷骰子得到的数为j,且这是连续出现的第k个j的情况数。

    代码:

    const int p = 1000000007;
    class Solution {
    public:
    	int dieSimulator(int n, vector<int>& rollMax) {
    		rollMax.insert(rollMax.begin(), 0);
    		long long ans[7][16], s[7] = { 0 };
    		memset(ans, 0, sizeof(ans));
    		for (int i = 1; i <= 6; i++)s[i] = ans[i][1] = 1, s[0] += s[i];
    		while (--n)
    		{
    			for (int i = 1; i <= 6; i++)
    			{
    				ans[i][0] = ans[i][rollMax[i]];
    				for (int j = rollMax[i]; j > 1; j--)ans[i][j] = ans[i][j - 1];
    				ans[i][1] = s[0] - s[i];
    			}
    			s[0] = 0;
    			for (int i = 1; i <= 6; i++)
    			{
    				s[i] = (s[i] + ans[i][1] - ans[i][0]) % p, s[0] += s[i];
    			}
    		}
    		return (s[0]%p + p) % p;
    	}
    };

    时间复杂度:n * rollMax.length * rollMax.getMax()? 空间复杂度:rollMax.length * rollMax.getMax()

    ?

    思路三:解空间平移

    把解空间的一个维度(这里的ans[j][k]的k这个维度)卷起来,收尾相接,从平面变成圆柱面。

    一般的思路都是用解空间的绝对坐标进行对应,这里我用相对坐标,n每变化一次,解空间就平移一个单位,从而避免了ans数组的平移操作。

    可以用n直接进行平移计算,不过为了程序好写好理解,我加了一个key数组。

    key数组的含义是:把圆柱面沿着k=key这条直线剪开,得到的平面就是解空间的平面,用key的平移代替了数组的平移。

    代码:

    const int p = 1000000007;
    class Solution {
    public:
    	int dieSimulator(int n, vector<int>& rollMax) {
    		rollMax.insert(rollMax.begin(), 0);
    		long long ans[7][16], s[7] = { 0 }, key[7];
    		memset(ans, 0, sizeof(ans));
    		for (int i = 1; i <= 6; i++)s[i] = ans[i][1] = 1, s[0] += s[i], key[i] = rollMax[i];
    		while (--n)
    		{			
    			for (int i = 1; i <= 6; i++)
    			{
    				ans[i][0] = ans[i][key[i]], ans[i][key[i]] = s[0] - s[i];
    				key[i] = (key[i] == 1) ? rollMax[i] : key[i]-1;
    			}
    			s[0] = 0;
    			for (int i = 1; i <= 6; i++)
    			{
    				s[i] = (s[i] + ans[i][key[i] % rollMax[i] + 1] - ans[i][0]) % p, s[0] += s[i];
    			}
    		}
    		return (s[0]%p + p) % p;
    	}
    };

    时间复杂度:n * rollMax.length? 空间复杂度:rollMax.length * rollMax.getMax()

    CSU - 1750 切蛋糕

    题目:


    Description

    小明考试得了高分,妈妈很高兴,就买了蛋糕奖励小明,蛋糕呈矩形状,且上方有细小的纹路将蛋糕分成了??n*m小块,但是分割蛋糕必须要沿着纹路进行切割,每次切割都会消耗一定的体力值,消耗的体力值为切割处蛋糕的单位长度??(例如在??3*4的蛋糕处切割蛋糕分成??3*1?和??3*3两块那么消耗的体力值就是 3),妈妈担心小明吃太多甜食对身体不好,所以事先规定了小明只能吃??k单位小块的蛋糕??( k不大于??n*m )?。小明吃蛋糕必须整个一大块吃下去,如果只吃一部分必须要切割出来才行,他不想消耗过多体力,所以他希望你帮他算算他吃到??k单位小块蛋糕最少消耗多少体力
    Input

    第一行输入一个??T,表示数据组数
    (T<=1000)
    接下来每组数据输入??3个正整数??n , m , k (n,m<=30 , k<=50)
    Output

    每组数据输出一个消耗的最小体力值
    Sample Input

    4
    2 2 1
    2 2 3
    2 2 2
    2 2 4
    Sample Output

    3
    3
    2
    0

    这个题目必须用动态规划来做。

    像这种三维甚至超过三维的,我更倾向于备忘录的方法。

    因为这样就不需要根据三维解空间的结构进行遍历,思路会简单一些。

    可以看出来,main函数是很简单的。

    搜索函数f主要是2个问题,

    第一,如何设置边界,这个需要仔细考虑,少了一种情况就会出错,或者死循环。

    第二,递归表达式怎么写。这个倒是不难,因为当你想到用动态规划做一个题目的时候,你已经基本上清楚了递归式是怎样的了。

    代码:
    ?

    #include<iostream>
    #include<string.h>
    using namespace std;
    
    int num[31][31][51];
    
    int f(int n, int m, int k)
    {
    	if (num[n][m][k]>-3000)return num[n][m][k];
    	if (n*m == k)return 0;
    	if (n*m < k)return -2000;
    	if (k == 0)return 0;
    	if (k < 0)return -2000;
    	int s = 1000, t;
    	for (int i = 1; i < n; i++)
    	{
    		for (int j = 0; j <= i*m && j <= k; j++)
    		{
    			t = f(i, m, j) + f(n - i, m, k - j) + m;
    			if (t>=0 && s>t)s = t;
    		}
    	}
    	for (int i = 1; i < m; i++)
    	{
    		for (int j = 0; j <= i*n && j <= k; j++)
    		{
    			t = f(n, i, j) + f(n, m - i, k - j) + n;
    			if (t>=0 && s>t)s = t;
    		}
    	}
    	num[n][m][k] = s;
    	return s;
    }
    
    int main()
    {
    	for (int i = 0; i < 31; i++)for (int j = 0; j < 31; j++)
    		for (int k = 0; k < 51; k++)num[i][j][k] = -3000;
    	int t, n, m, k;
    	cin >> t;
    	while (t--)
    	{
    		cin >> n >> m >> k;
    		cout << f(n, m, k) << endl;
    	}
    	return 0;
    }
    

    力扣?1420. 生成数组

    ?

    给你三个整数 n、m 和 k 。下图描述的算法用于找出正整数数组中最大的元素。

    请你生成一个具有下述属性的数组 arr :

    arr 中有 n 个整数。
    1 <= arr[i] <= m 其中 (0 <= i < n) 。
    将上面提到的算法应用于 arr ,search_cost 的值等于 k 。
    返回上述条件下生成数组 arr 的 方法数 ,由于答案可能会很大,所以 必须 对 10^9 + 7 取余。

    ?

    示例 1:

    输入:n = 2, m = 3, k = 1
    输出:6
    解释:可能的数组分别为 [1, 1], [2, 1], [2, 2], [3, 1], [3, 2] [3, 3]
    示例 2:

    输入:n = 5, m = 2, k = 3
    输出:0
    解释:没有数组可以满足上述条件
    示例 3:

    输入:n = 9, m = 1, k = 1
    输出:1
    解释:可能的数组只有 [1, 1, 1, 1, 1, 1, 1, 1, 1]
    示例 4:

    输入:n = 50, m = 100, k = 25
    输出:34549172
    解释:不要忘了对 1000000007 取余
    示例 5:

    输入:n = 37, m = 17, k = 7
    输出:418930126
    ?

    提示:

    1 <= n <= 50
    1 <= m <= 100
    0 <= k <= n

    ?

    思路:三维DP

    int p=1000000007;
    long long res[51][101][51];
    class Solution {
    public:
        long long numOfArrays(int n, int m, int k) {
             if(n<=0||m<=0||k<=0||k>n||k>m)return 0;
             if(n==1)return m;
             if(m==1)return 1;
             if(res[n][m][k])return res[n][m][k];
             long long ans=numOfArrays(n,m-1,k)+(numOfArrays(n-1,m,k)-numOfArrays(n-1,m-1,k))*m%p+numOfArrays(n-1,m-1,k-1);
             return res[n][m][k] = (ans%p+p)%p;
        }
    };

    ?

    递推式解释:

    如果最后一个数不是m,那就是numOfArrays(n,m-1,k)

    如果最后一个数是m,前面出现过m,那就是(numOfArrays(n-1,m,k)-numOfArrays(n-1,m-1,k))*m

    如果最后一个数是m,前面没有出现过m,那就是numOfArrays(n-1,m-1,k-1)

    力扣 1473. 给房子涂色 III

    ?

    在一个小城市里,有?m?个房子排成一排,你需要给每个房子涂上 n?种颜色之一(颜色编号为 1 到 n?)。有的房子去年夏天已经涂过颜色了,所以这些房子不需要被重新涂色。

    我们将连续相同颜色尽可能多的房子称为一个街区。(比方说 houses = [1,2,2,3,3,2,1,1] ,它包含 5 个街区??[{1}, {2,2}, {3,3}, {2}, {1,1}] 。)

    给你一个数组?houses?,一个?m * n?的矩阵?cost?和一个整数?target?,其中:

    houses[i]:是第?i?个房子的颜色,0?表示这个房子还没有被涂色。
    cost[i][j]:是将第?i?个房子涂成颜色?j+1?的花费。
    请你返回房子涂色方案的最小总花费,使得每个房子都被涂色后,恰好组成?target?个街区。如果没有可用的涂色方案,请返回?-1?。

    ?

    示例 1:

    输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
    输出:9
    解释:房子涂色方案为 [1,2,2,1,1]
    此方案包含 target = 3 个街区,分别是 [{1}, {2,2}, {1,1}]。
    涂色的总花费为 (1 + 1 + 1 + 1 + 5) = 9。
    示例 2:

    输入:houses = [0,2,1,2,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
    输出:11
    解释:有的房子已经被涂色了,在此基础上涂色方案为 [2,2,1,2,2]
    此方案包含 target = 3 个街区,分别是 [{2,2}, {1}, {2,2}]。
    给第一个和最后一个房子涂色的花费为 (10 + 1) = 11。
    示例 3:

    输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[1,10],[10,1],[1,10]], m = 5, n = 2, target = 5
    输出:5
    示例 4:

    输入:houses = [3,1,2,3], cost = [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], m = 4, n = 3, target = 3
    输出:-1
    解释:房子已经被涂色并组成了 4 个街区,分别是 [{3},{1},{2},{3}] ,无法形成 target = 3 个街区。
    ?

    提示:

    m == houses.length == cost.length
    n == cost[i].length
    1 <= m <= 100
    1 <= n <= 20
    1 <= target?<= m
    0 <= houses[i]?<= n
    1 <= cost[i][j] <= 10^4

    ?

    我比赛提交的的代码:

    int maxint=1234567;
    class Solution {
    public:
        int ans[105][22][105];
        int dp(vector<int>& houses, vector<vector<int>>& cost, int m, int n,int nn, int target) {
            if(m==0)
            {
                if(target!=1)return maxint;
                if(houses[m]>0)return 0;
                return cost[0][nn-1];
            }
            if(target<=0)return maxint;
            if(ans[m][nn][target]<maxint)return ans[m][nn][target];
            if(houses[m]>0 && houses[m]!=nn)return maxint;
            for(int ni=1;ni<=n;ni++)ans[m][nn][target]=min(ans[m][nn][target],((houses[m]>0)?0:cost[m][nn-1])+dp(houses,cost,m-1,n,ni,target-(ni!=nn)));
            return ans[m][nn][target];
        }
        int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
            for(int i=0;i<=m;i++)for(int j=0;j<=n;j++)for(int k=0;k<=target;k++)ans[i][j][k]=maxint;
            int r=maxint;
            for(int ni=1;ni<=n;ni++)r=min(r,dp(houses,cost,m-1,n,ni,target));
            if(r==maxint)return -1;
            return r;
        }
    };

    结果是解答错误,加上第九行

    if(houses[m]>0 && houses[m]!=nn)return maxint;

    解决了计算错误的问题,但是超时了。

    仔细一分析,在于我的maxint这个值既用作初始值,也用作表示无解的-1,造成了混乱。

    要区分这个,我加了一行,改了一行,代码变成:

    int maxint=1234567;
    class Solution {
    public:
        int ans[105][22][105];
        int dp(vector<int>& houses, vector<vector<int>>& cost, int m, int n,int nn, int target) {
            if(m==0)
            {
                if(target!=1)return maxint;
                if(houses[m]>0 && houses[m]!=nn)return maxint;  add
                if(houses[m]>0)return 0;
                return cost[0][nn-1];
            }
            if(target<=0)return maxint;
            if(ans[m][nn][target]<maxint)return ans[m][nn][target];
            if(houses[m]>0 && houses[m]!=nn)return maxint;
            ans[m][nn][target]--;
            for(int ni=1;ni<=n;ni++)ans[m][nn][target]=min(ans[m][nn][target],((houses[m]>0)?0:cost[m][nn-1])+dp(houses,cost,m-1,n,ni,target-(ni!=nn)));
            return ans[m][nn][target];
        }
        int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
            for(int i=0;i<=m;i++)for(int j=0;j<=n;j++)for(int k=0;k<=target;k++)ans[i][j][k]=maxint;
            int r=maxint;
            for(int ni=1;ni<=n;ni++)r=min(r,dp(houses,cost,m-1,n,ni,target));
            if(r>maxint-2)return -1;
            return r;
        }
    };
    
    下一篇:没有了