1. 程式人生 > 實用技巧 >P4158 [SCOI2009]粉刷匠

P4158 [SCOI2009]粉刷匠

題目連結

我們不妨先考慮只有一行的情形。

我們做兩個字首和\(red_i,bule_i\)分別表示前\(i\)個裡有多少個紅色塊和藍色塊。

\(f[i][k]\)為做到第\(i\)塊,此時用了\(k\)次塗刷的最大收益。

我們思考如下問題:既然重複塗色沒有收益,那麼我們強制讓我們的塗色方案沒有重疊的情況,即讓我們對於這一行的方案如下圖:

可以看出一段木塊被分割成若干塊(無色代表沒塗色。

我們只要從\(i\)向前列舉上一段的終點在哪即可轉移,對於\(i\)這塊不塗色的情況我們直接拿\(i - 1\)的答案來覆蓋即可

所以對於一條木塊我們有了\(O(m^3)\)的做法來求出\(f\)

		for(int r = 1;r <= m;++r){
			for(int k = 1;k <= m;++k){
				f[r][k] = f[r - 1][k];
				for(int l = 1;l <= r;++l){
					f[r][k] = std::max(f[l - 1][k - 1] + red[r] - red[l - 1],f[r][k]);
					f[r][k] = std::max(f[l - 1][k - 1] + bule[r] - bule[l - 1],f[r][k]);	
				}
			}
		}

我們接下來考慮我們做完了一條木塊怎麼統計答案:

	 	for(int to = t;to >= 0;-- to)
	 	for(int k = 0;k <= std::min(m,(ll)to);++k)
	 	fans[to] = std::max(fans[to],fans[to - k] + f[m][k]),ans = std::max(ans,fans[to]); 

類似於一維揹包即可。(注意列舉\(k\)時不要超出陣列\(f\)的大小,因為這個我調了好久)

所以最後的複雜度是\(O(n * (m ^ 3 + tm))\)

喜聞樂見的程式碼環節

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long

ll n,m,t,ans = 0,f[55][55];
ll red[55],bule[55];
ll fans[2505];

void init(){
	memset(f,0,sizeof(f));
	memset(red,0,sizeof(red));
	memset(bule,0,sizeof(bule));
}

int main(){
//	freopen("q.in","r",stdin);
//	freopen("q.out","w",stdout);
	memset(fans,0,sizeof(fans));
	scanf("%lld%lld%lld",&n,&m,&t);
	for(int i = 1;i <= n;++i){
		init();
		char s[55];
		scanf("%s",s + 1);
		for(int i = 1;i <= m;++i){
			red[i] = red[i - 1];
			bule[i] = bule[i - 1];
			if(s[i] == '0')
			red[i] ++ ;
			else
			bule[i] ++ ;
		}
		for(int r = 1;r <= m;++r){
			for(int k = 1;k <= m;++k){
				f[r][k] = f[r - 1][k];
				for(int l = 1;l <= r;++l){
					f[r][k] = std::max(f[l - 1][k - 1] + red[r] - red[l - 1],f[r][k]);
					f[r][k] = std::max(f[l - 1][k - 1] + bule[r] - bule[l - 1],f[r][k]);	
				}
			}
		}
//		for(int i = 1;i <= m;++i,puts(""))
//		for(int k = 1;k <= m;++k)
//		std::cout<<f[i][k]<<" ";
	 	for(int to = t;to >= 0;-- to)
	 	for(int k = 0;k <= std::min(m,(ll)to);++k)
	 	fans[to] = std::max(fans[to],fans[to - k] + f[m][k]),ans = std::max(ans,fans[to]); 
	}
	std::cout<<ans<<std::endl; 
}