1. 程式人生 > >bzoj1296(SCOI2009)粉刷匠

bzoj1296(SCOI2009)粉刷匠

分組背包 不用 pri bzoj 分區 targe 區間 ring include

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=1296

這道題暴露出自己:

  1.對於區間與前綴的可轉化性認識不足;

  2.對於分組背包不夠熟練。

很容易想到最後是一個分組背包,枚舉總共刷 k 次,前幾個木條刷了 k - j 次,當前木條刷 j 次,得到答案。

所以我們需要每個木條“刷k次的最大正確數量”這一個值。

  自己只會做“前綴被全刷完的最少次數”(顏色相同就-1),於是想把它轉化成這個問題,就是算出前綴被刷完的最小次數,用它更新“刷k次的最大數量”。

    但是又覺得不一定是前綴被刷完,可以是區間被刷完。於是嘗試把那個寫法搬到區間上;當然很混亂。

1.其實“被刷完”這個狀態不夠靈活,不如就定義成“刷k次的最大數量”;只要想到(2)和(3),就知道這個定義還是可以轉移的。

2.如果是這個定義,用前綴的狀態遞推就足夠,因為不一定每個都刷了顏色,就不用刻意分區間了。

3.在求前綴的時候就是指定後 j 個只刷一次。這種劃分的方法應該很熟悉它的正確性才行。

4.分組背包可以在邊算dp[i]的時候就邊維護好。就算不這樣而最後單獨求一下,其實也挺好弄的。自己應該對它更熟練。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace
std; const int N=55,T=2505; int n,m,t,a[N][N],dp[N][T],s[N],ans,f[N][T]; int cal(int x,int y) { int a=s[y]-s[x-1]; int b=y-x+1-a; return max(a,b); } int main() { scanf("%d%d%d",&n,&m,&t); for(int i=1;i<=n;i++) { memset(s,0,sizeof s); // memset(dp,0,sizeof dp);
for(int j=1;j<=m;j++) { scanf("%1d",&a[i][j]);s[j]=s[j-1]+(a[i][j]); for(int k=1;k<=j;k++) { dp[j][k]=0; for(int l=0;l<j;l++) dp[j][k]=max(dp[j][k],dp[l][k-1]+cal(l+1,j)); } } for(int k=1;k<=t;k++) //k<=t for(int j=0;j<=k&&j<=m;j++) f[i][k]=max(f[i][k],f[i-1][k-j]+dp[m][j]); } for(int i=1;i<=t;i++)ans=max(ans,f[n][i]); printf("%d",ans); return 0; }

bzoj1296(SCOI2009)粉刷匠