LintCode 房屋染色II

问题描述:

这里有n个房子在一列直线上,现在我们需要给房屋染色,共有k种颜色。每个房屋染不同的颜色费用也不同,你需要设计一种染色方案使得相邻的房屋颜色不同,并且费用最小。

费用通过一个nxk 的矩阵给出,比如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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值