1. 程式人生 > >P1896 [SCOI2005]互不侵犯

P1896 [SCOI2005]互不侵犯

long orange 滿足 多少 相加 找到 個數 方案 col

題目

題目描述

在N×N的棋盤裏面放K個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各一個格子,共8個格子。

註:數據有加強(2018/4/25)

輸入輸出格式

輸入格式:

只有一行,包含兩個數N,K ( 1 <=N <=9, 0 <= K <= N * N)

輸出格式:

所得的方案數

輸入輸出樣例

輸入樣例#1: 復制
3 2
輸出樣例#1: 復制
16

分析

  • 由於上面的種類多,所以我們就要用到狀壓DP

  • 首先,我們要先考慮三個問題
  • 上下左右不能放重復
  • 上下不能能放重復可以這樣解決 直接兩行之間and 如果結果為0就是可行的做法
  • 左右我們可以講一行進行>>1和<<1再分別and 查看是否有一個出現為真值的情況
  • 在枚舉之前先找到所以滿足左右的就可以減少時間了
  • 最後答案在最後一行全部相加即可

代碼

 1 #include<iostream>
 2 using namespace std;
 3 long long n,k,t[100001];
 4 long long f[10][100001][101];
 5 long long sta[100001],king[100001],ans;
6 void inte() 7 { 8 int tot=(1<<n)-1; 9 for(int i=0;i<=tot;i++) 10 if(!((i<<1)&i)) 11 { 12 sta[++ans]=i; 13 int t=i; 14 while(t) 15 { 16 king[ans]+=t%2; 17 t>>=1
; 18 } 19 } 20 } 21 int main () 22 { 23 ios::sync_with_stdio(false); 24 cin>>n>>k; 25 inte(); 26 for (int i=1;i<=ans;i++) 27 if (king[i]<=k) 28 f[1][i][king[i]]=1; 29 for (int i=2;i<=n;i++) 30 for (int j=1;j<=ans;j++) 31 for (int kk=1;kk<=ans;kk++) 32 { 33 if (sta[j]&sta[kk]) continue; 34 if (sta[j]&(sta[kk]<<1)) continue; 35 if ((sta[j]<<1)&sta[kk]) continue; 36 for (int s=1;s<=k;s++) 37 { 38 if (s+king[j]>k) continue; 39 f[i][j][king[j]+s]+=f[i-1][kk][s]; 40 } 41 } 42 long long sum=0; 43 for (int i=1;i<=n;i++) 44 for (int j=1;j<=ans;j++) 45 sum+=f[i][j][k]; 46 cout<<sum; 47 }

P1896 [SCOI2005]互不侵犯