题目链接:房屋染色
题意
我简化一下题意,给你一个n*k的矩阵,我们从每一行选取一个数使得n行选取这n个数之和最小,对于选取有一个限制,如果连续行选取的数在同一列,最多取t行,剩下相邻的行不能取同一列的数,问最小值是多少。
题解
由题意得,1≤n,k≤100,本题貌似没有什么贪心策略,看来只能优雅的暴力dp登场了。
很明显dp需要三维才能符合本题的操作。
定义:dp[i][j][k]:前i行,最后一行选取第j列,此时符合k情况时最小值。
(k有两种情况,0代表没有出现连续行选取同一列,1代表已经在连续行选取同一列的情况。)
初始化: d p [ 0 ] [ j ] [ 0 ] = d p [ 0 ] [ j ] [ 1 ] = c o s t s [ 0 ] [ j ] ( j ∈ [ 0 , m ) ) {dp[0][j][0]=dp[0][j][1]=costs[0][j] (j\in [0,m))} dp[0][j][0]=dp[0][j][1]=costs[0][j](j∈[0,m))
(从第0行第0列开始)
状态转移:
d p [ i ] [ j ] [ 0 ] = m i n ( d p [ i − 1 ] [ k ] [ 0 ] ) + c o s t s [ i ] [ j ] ( k ∈ [ 0 , m ) a n d ( k ≠ j ) {dp[i][j][0]=min(dp[i-1][k][0])+costs[i][j] (k\in[0,m)and(k≠j)} dp[i][j][0]=min(dp[i−1][k][0])+costs[i][j](k∈[0,m)and(k=j)
d p [ i ] [ j ] [ 1 ] = m i n ( d p [ i − 1 ] [ k ] [ 1 ] ) + c o s t s [ i ] [ j ] ( k ∈ [ 0 , m ) a n d ( k ≠ j ) {dp[i][j][1]=min(dp[i-1][k][1])+costs[i][j] (k\in[0,m)and(k≠j)} dp[i][j][1]=min(dp[i−1][k][1])+costs[i][j](k∈[0,m)and(k=j)
注意k从0转移为1时,很明显需要枚举第i行开始,向前1~t行都选第j列的情况。
所以需要统计一个前缀和,sum[i][j]:第j列,前i行的值之和。
d p [ i ] [ j ] [ 1 ] = m i n ( d p [ l ] [ j ] [ 0 ] + s u m [ i ] [ j ] − s u m [ l ] [ j ] ) ( l ∈ [ 0 , i − 1 ] a n d ( i − l + 1 ≤ t ) ) {dp[i][j][1]=min(dp[l][j][0]+sum[i][j]-sum[l][j]) (l\in[0,i-1]and(i-l+1≤t))} dp[i][j][1]=min(dp[l][j][0]+sum[i][j]−sum[l][j])(l∈[0,i−1]and(i−l+1≤t))
最后答案就是: m i n ( d p [ n − 1 ] [ j ] [ 0 ] , d p [ n − 1 ] [ j ] [ 1 ] ) , j ∈ [ 0 , m ) {min(dp[n-1][j][0],dp[n-1][j][1]),j\in[0,m)} min(dp[n−1][j][0],dp[n−1][j][1]),j∈[0,m)
本题的难点在于想出dp的定义以及状态转移方程,多加练习+思考才能在此类多维dp中得胜。
代码
int inf=0x3f3f3f;
class Solution {
public:
/**
* @param costs: costs of paint ith house into color j
* @param t: maximum length of street
* @return: minimum costs of painting all houses
*/
int dp[105][105][2];
int sum[105][105];
int paintHouseIII(vector<vector<int> > &costs, int t) {
// Write your code here.
memset(dp, inf, sizeof(dp));
int n=costs.size(),m=costs[0].size();
for(int j=0;j<m;j++) dp[0][j][0]=dp[0][j][1]=costs[0][j];
for(int j=0;j<m;j++)
for(int i=0;i<n;i++)
{
if(i==0) sum[i][j]=costs[i][j];
else sum[i][j]=sum[i-1][j]+costs[i][j];
}
for(int i=1;i<n;i++)
for(int j=0;j<m;j++)
{
for(int k=0;k<m;k++)
{
if(j!=k)
{
dp[i][j][0]=min(dp[i-1][k][0]+costs[i][j], dp[i][j][0]);
dp[i][j][1]=min(dp[i-1][k][1]+costs[i][j], dp[i][j][1]);
}
}
for(int l=i-1;l>=0;l--)
{
if(i-l+1>t) break;
dp[i][j][1]=min(dp[i][j][1],dp[l][j][0]+sum[i][j]-sum[l][j]);
}
}
int ans=inf;
for(int j=0;j<m;j++)
{
ans=min(ans,dp[n-1][j][0]);
ans=min(ans,dp[n-1][j][1]);
}
return ans;
}
};