1. 程式人生 > >【狀壓dp】CDOJ1608 暑假集訓

【狀壓dp】CDOJ1608 暑假集訓

algo name pac 開始 技術分享 只需要 memset urn cnblogs

技術分享

裸的狀壓的話,很顯然……但有一個強大的優化。

就是在枚舉決策的時候,固定第一個空位置。可以證明,這樣狀態數沒有減少,但是降低了很多重復訪問。

因為你在枚舉的時候,總是可以劃分為包含第一個空位置的3個位置;以及不包含第一個空位置的三個位置。這樣固定先枚舉前者,避免了重復。

還有一個優化是,沒必要每次判斷當前集合是否合法。

因為被更新到過的才是合法的,只需要一開始置成-1,不合法的狀態一定不會被更新到。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,a[23][23][23],f[(1<<21)+10];
char c;
int ff;
inline void R(int &x){
    c=0;ff=1;
    for(;c<‘0‘||c>‘9‘;c=getchar())if(c==‘-‘)ff=-1;
    for(x=0;c>=‘0‘&&c<=‘9‘;c=getchar())(x*=10)+=(c-‘0‘);
    x*=ff;
}
//inline bool check(int S){
//	int res=0;
//	for(int i=0;i<n;++i){
//		res+=((S>>i)&1);
//	}
//	return res%3==0;
//}
int cans[23*23*23],o;
int main(){
	int x,y,z;
	R(n);
	for(int i=1;i<=n*(n-1)*(n-2)/6;++i){
		R(x); R(y); R(z);
		R(*(*(*(a+x-1)+y-1)+z-1));
	}
	memset(f,-1,sizeof(f));
	f[0]=0;
	for(int i=0;i<(1<<n);++i){
//		if(!check(i)){
//			continue;
//		}
		if(f[i]==-1){
			continue;
		}
		o=0;
		for(int j=0;j<n;++j){
			if(!((i>>j)&1)){
				cans[++o]=j;
			}
		}
		for(int k=2;k<=o-1;++k){
			for(int l=k+1;l<=o;++l){
				f[i|(1<<cans[1])|(1<<cans[k])|(1<<cans[l])]=max(f[i|(1<<cans[1])|(1<<cans[k])|(1<<cans[l])],f[i]+*(*(*(a+cans[1])+cans[k])+cans[l]));
			}
		}
	}
	printf("%d\n",f[(1<<n)-1]);
	return 0;
}

【狀壓dp】CDOJ1608 暑假集訓