问题描述:
这里有n
个房子在一列直线上,现在我们需要给房屋染色,共有k
种颜色。每个房屋染不同的颜色费用也不同,你需要设计一种染色方案使得相邻的房屋颜色不同,并且费用最小。
费用通过一个n
xk
的矩阵给出,比如cost[0][0]
表示房屋0
染颜色0
的费用,cost[1][2]
表示房屋1
染颜色2
的费用。
分析:
经典的动态规划问题,假设当前的二维数组是 :
[1,2,3]
[4,5,6]
[7,8,9]
有三座房子的情况:先生成动态规划数组 与原数组大小相同dp[][];
dp[i][j]的含义代表在第i个房子选了j颜色时候 所需要最小的花费
如果只有一所房子的时候,房子的花费就只有三种情况
[1,2,3] 将这三种情况赋给dp[0] 也就是动态规划数组的第一行
第二行的时候要选择就去选取与当前颜色不同,但花费最小的颜色
因为当前只有三种颜色所以 就要去选择剩下两种颜色花费最小的那一个
dp[i][j]=
costs[i][j]+min(dp[i-1][1......n]);//n!=j;
动态规划第二行就是[6,6,7]
同理就可以得到整个dp[][]数组,dp[][]最后一行中最小的数就是花费最少的
但是 如果颜色的数量较多,时间复杂度就会提高到O(n*k*k) 就会超时;
我们可以在第一次遍历dp[i-1]数组的时候把 dp[i-1]这段数组中的最小值
和次小值以及他们的下标保存下来;当我们选取最小花费的时候 先看颜色
是不是和最小花费的房屋重复了,如果重复,我们就去累加次小值;
如果不重复就累加最小值。
这样可以保证 时间复杂度降到O(n*k)
源码如下:
int minCostII(vector<vector<int > >& costs)
{
// Write your code here
if( costs.empty() || costs[0].empty() || costs[0].size() < 2 ) return 0;
int lenRow = costs.size();
int lenColumn = costs[0].size();
vector<vector<int > > dp( lenRow,vector<int >( lenColumn,0));
for( int i=0 ; i<lenColumn ; i++ )
dp[0][i] = costs[0][i];
for( int i=1; i<lenRow ; i++)
{
int minValue , nextMinValue;
int minIndex , nextMinIndex;
minValue = dp[i-1][0] <= dp[i-1][1] ? dp[i-1][0]:dp[i-1][1];
nextMinValue = dp[i-1][0] >= dp[i-1][1] ? dp[i-1][0]:dp[i-1][1];
minIndex = dp[i-1][0] <= dp[i-1][1] ? 0:1;
nextMinIndex = dp[i-1][0] >= dp[i-1][1] ? 0:1;
for( int j=2;j < lenColumn ; j++ )
{
if(dp[i-1][j] <= minValue)
{
nextMinValue = minValue;
nextMinIndex = minIndex;
minIndex = j;
minValue = dp[i-1][j];
}
else if( dp[i-1][j] > minValue&&dp[i-1][j] < nextMinValue)
{
nextMinIndex = j;
nextMinValue = dp[i-1][j];
}
}
for( int n=0;n < lenColumn;n++)
{
if( n == minIndex )
dp[i][n] = nextMinValue + costs[i][n];
else
dp[i][n] = minValue + costs[i][n];
}
}
int min = dp[lenRow-1][0];
for( int i=1;i<lenColumn;i++)
{
if( min > dp[lenRow-1][i] )
min = dp[lenRow-1][i];
}
return min;
}
我一直觉得lintcode这个oj很奇怪。。。。本来上面这段代码是AC的但是又卡到了94%。。。
对上面的代码进行分析发现在更新dp的过程中,先通过了一个for循环来获取上一层的最小值和
次小值。。。那么实际的时间复杂度就变成了O(n*k*2)题目要求时间复杂度是O(n*k)所以有些问题
我们可以通过在累加dp数组的过程中进行获取最小值和次小值。在这一过程当中首先要对第一行的数据进行
初始化 包括最小值和次小值。。。通过只有两个元素的小数组来滚动更新。
int minCostII(vector<vector<int > >& costs)
{
// Write your code here
if( costs.empty() || costs[0].empty() || costs[0].size()<2 )return 0;
int lenRow = costs.size();
int lenColumn = costs[0].size();
vector<vector<int > > dp(lenRow,vector<int >(lenColumn,0));
for( int i=0;i<lenColumn;i++)
dp[0][i] = costs[0][i];
int minValue[2] ,nextMinValue[2];
int minIndex[2];
minValue[0] = minValue[1] = dp[0][0] > dp[0][1] ? dp[0][1]:dp[0][0];
minIndex[0] = minIndex[1] = dp[0][0] > dp[0][1] ? 1:0;
nextMinValue[0] = nextMinValue[1] = dp[0][0] >= dp[0][1] ? dp[0][0]:dp[0][1];
for( int i=2;i < lenColumn ;i++)
{
if(costs[0][i]<minValue[0])
{
nextMinValue[0]=nextMinValue[1]=minValue[0];
minValue[0]=minValue[1]=costs[0][i];
minIndex[0]=minIndex[1]=i;
}
else if(costs[0][i]<=minValue[0]&&costs[0][i]<nextMinValue[0])
{
nextMinValue[0]=nextMinValue[1]=costs[0][i];
}
}
if(costs.size()==1)
return minValue[0];
int n=1;
for(int i=1;i<lenRow;i++)
{
for(int j=0;j<lenColumn;j++)
{
if(j==minIndex[1-n])
dp[i][j]=nextMinValue[1-n]+costs[i][j];
else
{
dp[i][j]=minValue[1-n]+costs[i][j];
}
if(j>=1)
{
if(j==1)
{
minValue[n]=dp[i][0]>dp[i][1]?dp[i][1]:dp[i][0];
minIndex[n]=dp[i][0]>dp[i][1]?1:0;
nextMinValue[n]=dp[i][0]>=dp[i][1]?dp[i][0]:dp[i][1];
}
else
{
if(dp[i][j]<minValue[n])
{
nextMinValue[n]= minValue[n];
minValue[n] = dp[i][j];
minIndex[n] = j;
}
else if(dp[i][j]>=minValue[n]&&dp[i][j]<nextMinValue[n])
{
nextMinValue[n] = dp[i][j];
}
}
}
}
n = 1-n;
}
int min = INT_MAX;
for( int i = 0;i <lenColumn ; i++)
if( min > dp[lenRow-1][i])
min = dp[lenRow-1][i];
return min;}
虽然到这里已经可以AC了 但是我发现还是有可以优化的余地,将空间复杂度优化成O(n);
我们的可以直接把dp数组的第一维去掉
int minCostII(vector<vector<int > >& costs)
{
// Write your code here
if ( costs.empty() || costs[0].empty() || costs[0].size() < 2 )
return 0;
int lenRow = costs.size();
int lenColumn = costs[0].size();
vector<int > dp( lenColumn,0);
for(int i = 0;i<lenColumn;i++)
dp[i] = costs[0][i];
int minValue[2] ,nextMinValue[2];
int minIndex[2];
minValue[0] = minValue[1] = dp[0] > dp[1]?dp[1]:dp[0];
minIndex[0] = minIndex[1] = dp[0] > dp[1]?1:0;
nextMinValue[0]=nextMinValue[1]=dp[0]>=dp[1]?dp[0]:dp[1];
for(int i=2;i<lenColumn;i++)
{
if(costs[0][i]<minValue[0])
{
nextMinValue[0] =nextMinValue[1] = minValue[0];
minValue[0] =minValue[1] = costs[0][i];
minIndex[0] =minIndex[1] = i;
}
else if(costs[0][i] <= minValue[0]&&costs[0][i]<nextMinValue[0])
{
nextMinValue[0] = nextMinValue[1] = costs[0][i];
}
}
if( costs.size() == 1)
return minValue[0];
int n = 1;
for( int i=1;i<lenRow;i++)
{
for( int j=0;j<lenColumn;j++)
{
if( j == minIndex[1-n])
dp[j] = nextMinValue[1-n]+costs[i][j];
else
{
dp[j] = minValue[1-n]+costs[i][j];
}
if(j >=1 )
{
if( j==1 )
{
minValue[n] = dp[0] > dp[1]?dp[1]:dp[0];
minIndex[n] = dp[0] > dp[1]?1:0;
nextMinValue[n] = dp[0] >= dp[1]?dp[0]:dp[1];
}
else
{
if( dp[j] < minValue[n])
{
nextMinValue[n]= minValue[n];
minValue[n] = dp[j];
minIndex[n] = j;
}
else if(dp[j] >= minValue[n]&&dp[j] < nextMinValue[n])
{
nextMinValue[n] = dp[j];
}
}
}
}
n = 1-n;
}
int min = INT_MAX;
for(int i=0;i<lenColumn;i++)
if(min > dp[i])
min = dp[i];
return min;
}