裸的状压的话,很显然……但有一个强大的优化。
就是在枚举决策的时候,固定第一个空位置。可以证明,这样状态数没有减少,但是降低了很多重复访问。
因为你在枚举的时候,总是可以划分为包含第一个空位置的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 ",f[(1<<n)-1]); return 0; }