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;
}
标签:不涂,max,23,粉刷,SCOI2009,2022,P4158,dp
From: https://www.cnblogs.com/yyx525jia/p/16724198.html