粉刷匠
bzoj1296 粉刷匠 (洛谷P4158)
◦有n条木板要被粉刷,每条木板分为m个格子,每个格子需要被刷成蓝色
或红色。
◦每次粉刷可以在一条木板上给连续的一段格子刷上相同的颜色。每个格
子最多被刷一次。
◦问若只能刷k次,最多正确粉刷多少格子。
◦ n,m<=50,k<=2500
dp题。dp方程较难推,有两个dp值,包含了从局部到整体的解题思路
当只有一条木板时,设g[i][j][k]表示第i个木板前j个格子刷k次的最大值
则g[i][j][k]=max{g[i][t][k-1]+w(t+1,j)}(t<j)
w(x,y)表示第x个到第y个格子的最多同色格子数(前缀和)
有多条木板时,设f[i][j]表示前i个木板刷j次的最大答案
f[i][j]=max{f[i-1][k]+g[i][m][j-k]}(k<=j)
这里引用zhhx的一句话:其实像这种一般的dp,就是把影响答案的信息用多维状态来表示,什么
必要什么就放在状态里。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,m,t;
int a[55][55];
int s1[55][55]={},s2[55][55]={};//s1:0 s2:1
int f[55][2550];
int g[55][55][55];
int wid(int i,int x,int y)
{
return max(s1[i][y]-s1[i][x-1],s2[i][y]-s2[i][x-1]);
}
int tim;
int main()
{
ios::sync_with_stdio(0);
cin>>n>>m>>tim;
for(int i=1;i<=n;i++)
{
string str;cin>>str;
for(int j=1;j<=str.size();j++)
{
if(str[j-1]=='0')a[i][j]=0;
else a[i][j]=1;
if(a[i][j]==0)s1[i][j]=s1[i][j-1]+1,s2[i][j]=s2[i][j-1];else s1[i][j]=s1[i][j-1],s2[i][j]=s2[i][j-1]+1;
}
}
for(int i=1;i<=n;i++)//第i个木板
for(int j=1;j<=m;j++)//前j个格子
for(int k=1;k<=j;k++)//刷了k次
for(int t=0;t<j;t++)//前t个格子
{
g[i][j][k]=max(g[i][j][k],g[i][t][k-1]+wid(i,t+1,j));
}
for(int i=1;i<=n;i++)
for(int j=1;j<=tim;j++)
for(int k=0;k<=j;k++)
{
int cnt=0;
if(j-k>=m)cnt=m;else cnt=g[i][m][j-k];
f[i][j]=max(f[i-1][k]+cnt,f[i][j]);
}
cout<<f[n][tim]<<endl;
return 0;
}