【BZOJ】【1087】【SCOI2005】互不侵犯King
状压DP
我写的太水了……64ms才过,估计还有更好的做法,希望各位神犇不吝赐教>_<。
嗯这题很明显每一行都可以用一个2进制数表示放置方式的,(1表示放,0表示不放)。然后预处理一下所有合法状态(同一行内的国王之间不会互相攻击),然后记f[i][j][k]为第i行,用第j种合法放置方式放国王,总共放了k个国王的方案数,转移的时候枚举上一行的状态,看是否和这一行的冲突(和预处理一样可以用位运算加速),然后累加即可,很基础的状压DP。
WA了一次的原因:最后答案可能会爆int,必须用longlong(或者unsigned int也行吧?)
1 /************************************************************** 2 Problem: 1087 3 User: ProgrammingApe 4 Language: C++ 5 Result: Accepted 6 Time:64 ms 7 Memory:8480 kb 8 ****************************************************************/ 9 10 //BZOJ 1087 11 #include<cstdio> 12 #include<cstring> 13 #include<cstdlib> 14 #include<iostream> 15 #include<algorithm> 16 #define rep(i,n) for(int i=0;i<n;++i) 17 #define F(i,j,n) for(int i=j;i<=n;++i) 18 #define D(i,j,n) for(int i=j;i>=n;--i) 19 using namespace std; 20 21 int n,K,num[1024],a[1024],cnt; 22 long long f[10][1024][90]; 23 24 int count(int x){ 25 int ans=0; 26 while(x){ans+=x&1; x>>=1;} 27 return ans; 28 } 29 30 int main(){ 31 // freopen("file.in","r",stdin); 32 scanf("%d%d",&n,&K); 33 F(i,0,(1<<n)-1){ 34 int s=count(i); 35 if ((s>K) || (i&(i>>1)) || (i&(i<<1))) continue; 36 a[++cnt]=i; 37 num[cnt]=s; 38 } 39 F(j,1,cnt) 40 f[1][j][num[j]]=1; 41 F(i,2,n) 42 F(j,1,cnt) 43 F(m,0,n*n) 44 F(k,1,cnt){ 45 if (f[i-1][j][m]==0) continue; 46 if ( (a[j]&a[k]) || (a[j]&(a[k]>>1)) || (a[j]&(a[k]<<1)) || m+num[k]>K) continue; 47 f[i][k][num[k]+m]+=f[i-1][j][m]; 48 } 49 long long ans=0; 50 F(j,1,cnt) ans+=f[n][j][K]; 51 printf("%lld\n",ans); 52 return 0; 53 }