【狀壓dp】互不侵犯KING
阿新 • • 發佈:2017-06-06
git algorithm long long true 求解 格子 ble bool span Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3866 Solved: 2264
[Submit][Status][Discuss]
3 2
互不侵犯KING
Submit: 3866 Solved: 2264
[Submit][Status][Discuss]
Description
在N×N的棋盤裏面放K個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上
左下右上右下八個方向上附近的各一個格子,共8個格子。
Input
只有一行,包含兩個數N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案數。
Sample Input
Sample Output
16HINT
Source
試題分析:狀壓dp,設dp[i][j][k]代表i*i的矩形放j個國王,此行狀態為k的二進制的種數
那麽容易得到轉移方程:dp[i][j][k]+=dp[i-1][j-cnt[k]][p]
其中cnt[k]表示k在二進制下1的數量,p表示枚舉的上一行的狀態
代碼
/*bzoj 1087 wxjor 2017.06.06 */ #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<stack> #include<vector> #include<algorithm> //#include<cmath> using namespace std; const int INF = 9999999; #define LL long long inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } int N,K; long long dp[10][100][1025]; int cansr[1025]; int tmp; int cnt[1025]; void pre(){//預處理所有可行狀態(在一行中KING互補侵犯) bool flag=true; for(int i=0;i<(1<<N);i++){ int a=0,sum=0; flag=true; int p=i; while(i){ if((i&1)&&a){ flag=false; break; } a=(i&1); if(a) sum++; i>>=1; } if(flag) cansr[++tmp]=p,cnt[tmp]=sum,dp[1][sum][p]=1;//計入 i=p; } return ; } bool check(int a,int b){//判斷兩行中是否會侵犯 if((a&b)||((a>>1)&b)||((a<<1)&b)||((b<<1)&a)||((b>>1)&a)) return false; return true; } long long ans; int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); N=read(),K=read(); pre(); for(int i=2;i<=N;i++){ for(int j=0;j<=K;j++)//一開始寫成了j=1 for(int k=1;k<=tmp;k++){ for(int p=1;p<=tmp;p++){ if(!check(cansr[k],cansr[p])) continue; if(cnt[k]+cnt[p]>j) continue;//枚舉的狀態超出放的數量 dp[i][j][cansr[k]]+=dp[i-1][j-cnt[k]][cansr[p]]; } } } for(int i=1;i<=tmp;i++) ans+=dp[N][K][cansr[i]];//求解答案 printf("%lld\n",ans); return 0; } //dp[i][j][k]+=dp[i-1][j-cnt(k)][k‘]
【狀壓dp】互不侵犯KING