做题记录整理dp11 P4158 [SCOI2009]粉刷匠(2022/9/23)

P4158 [SCOI2009]粉刷匠
事实上前半个小时我甚至没想用dp做。。。
感觉这道题难度标高了(跟那个想让我测出题人的码的题相比)
首先可以发现每一行之间都是独立的,所以先考虑把每一行的刷i次的最大正确数整出来
然后就可以想到dp(事实上我想了很久怎么贪心)

本蒟蒻不会什么太高深的做法,就猛写了一发毒瘤dp,莫名其妙过了

dp[i][j][k][0/1/2]表示第i行第j列一共粉刷了k次,0/1/2分别表示当前格子没有涂色/涂了错的颜色/涂了对的颜色, 然后我们考虑逐格转移:

当j=1也就是出于每行的第一个位置时,我们要考虑上一行的最后一个位置, 即

dp[i][j][k][0]=max(dp[i-1][m][k][1],max(dp[i-1][m][k][2],dp[i-1][m][k][0]));

dp[i][j][k][1]=max(dp[i-1][m][k-1][2],max(dp[i-1][m][k-1][1],dp[i-1][m][k-1][0]));

dp[i][j][k][2]=max(dp[i-1][m][k-1][2],max(dp[i-1][m][k-1][1],dp[i-1][m][k-1][0]))+1;
其余位置要考虑这个格子颜色是否和前一个格子的颜色相等,如果相等,就有

dp[i][j][k][2]=dp[i][j-1][k][2]+1;
可以直接接上

dp[i][j][k][1]=max(dp[i][j-1][k][1],dp[i][j-1][k-1][0]);
前面涂错或不涂

dp[i][j][k][0]=max(dp[i][j-1][k][0],dp[i][j-1][k][1]);
前面涂错或不涂 如果不相等,

dp[i][j][k][2]=max(dp[i][j-1][k-1][2],max(dp[i][j-1][k][1],dp[i][j-1][k-1][0]))+1;
前面可能有三种情况

dp[i][j][k][1]=max(dp[i][j-1][k][2],dp[i][j-1][k-1][0]);
涂对或不涂       

dp[i][j][k][0]=max(dp[i][j-1][k][0],dp[i][j-1][k][2]);
涂对或不涂
可以用滚动数组压掉第一维,这样空间复杂度是O(nT),时间复杂度是O(nmT),还是可以过的

参考这位大佬的题解,不知道为什么他的博客里是出错

#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
using namespace std;
int dp[2501][2505][3];
int a[5500];
int ans[5005];
int n,m,k;
int main()
{
	scanf("%d%d%d\n",&n,&m,&k);
	for1(l,1,n)
	{
		for1(i,1,m)
          	scanf("%1d",&a[i]);
		for1(i,1,m)
		    for1(j,1,k) 
		        dp[i][j][0]=0,dp[i][j][1]=0;
		        
		for1(i,1,m)
		{
			for1(j,1,i)
			{
			    dp[i][j][a[i]]=dp[i-1][j][a[i]]+1;
			    dp[i][j][!a[i]]=dp[i-1][j][!a[i]];
			    
			    dp[i][j][a[i]]=max(dp[i][j][a[i]],max(dp[i-1][j-1][0],dp[i-1][j-1][1])+1);
			    dp[i][j][!a[i]]=max(dp[i][j][!a[i]],max(dp[i-1][j-1][0],dp[i-1][j-1][1]));
			}
		}
		for(int i=k;i>=1;i--)//背包 
			for1(j,0,min(m,i))
				ans[i]=max(ans[i],ans[i-j]+max(dp[m][j][0],dp[m][j][1]));
	}
	cout<<ans[k];
	return 0;
}
		
posted @ 2022-09-23 20:46  yyx525jia  阅读(27)  评论(0)    收藏  举报