[超级码力在线编程大赛初赛(三)] 房屋染色 前缀和+多维dp

题目链接:房屋染色
题意

我简化一下题意,给你一个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[i1][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[i1][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,i1]and(il+1t)

最后答案就是: 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[n1][j][0],dp[n1][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;
	}
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值