洛谷傳送門
在n×n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。
注:資料有加強(2018/4/25)
只有一行,包含兩個數n,k ( 1 <=n <=9, 0 <= k <= n * n)
所得的方案數
輸入 #1複製
輸出 #1複製
原諒我一開始看到題還以為是爆搜。。。
其實是一道狀態壓縮的題目。
蒟蒻自己乙個比較大的進步就是把自己狀態設定對了...
設定:\(dp[i][j][k]\)為第\(i\)行狀態為\(j\)、已經用了\(k\)個國王時的方案數。
狀態壓縮大體有這麼幾步:設定狀態\(\rightarrow\)考慮轉移方式\(\rightarrow\)按轉移方式考慮預處理和判斷轉移條件\(\rightarrow\)開始轉移\(\rightarrow\)統計答案。
那麼我們設定好狀態,開始考慮轉移方式:我們發現,若是想從第\(i-1\)行開始轉移,轉移的條件一是當前和上一次的狀態,但是,這些狀態的改變必然還會改變國王的個數。也就是說,這陣列的兩維是有聯絡的,是自變數和因變數的關係。所以我們因此想到,既然是自變數和因變數的關係,我們莫不如由此構建乙個對映,存下來每個狀態和每個狀態需要的國王人數。這樣我們轉移的時候就沒啥問題了。
如何預處理呢?我們想到,我們需要按行處理狀態,每個狀態有放國王和不放國王兩種選擇。因為是預處理,我們是肯定不能用遞推和\(dp\)的(你想幹啥)
所以我們考慮搜尋。
一次搜尋可以處理出所有合法的行的方式。
這裡插一嘴,因為我們已經把所有合法的行的方式都求出來了,所以我們沒必要再把\(dp\)陣列的第二維開那麼大,構建好對映關係之後,直接用\(cnt\)代替這個二進位制狀態即可。(因為\(1-cnt\)的每個數都對應著乙個陣列\(s[i]\)作為狀態。)
然後再轉移的時候進行判斷是否合法就可以。
轉移方程:
\[dp[i][j][l]+=dp[i-1][k][l-num[j]]
\]這裡的\(k,j\)分別表示一種狀態。
**:
#include#define int long long
using namespace std;
int n,k,cnt,ans;
int s[100],num[100];
int dp[10][100][110];
//dp[i][j][k]表示前i-1行放完,第i行狀態為j、有k個國王時的方案數
//狀態0/1:0:國王攻擊不到;1:被國王占領
void dfs(int pos,int st,int tot)
dfs(pos+1,st,tot);
dfs(pos+2,st+(1<>1))
continue;
else if(s[j]&(s[k]<<1))
continue;
else
for(int l=num[j];l<=k;l++)
dp[i][j][l]+=dp[i-1][k][l-num[j]];
}ans=0;
for(int i=0;i<=cnt;i++)
ans+=dp[n][i][k];
printf("%lld",ans);
return 0;
}
SCOI2005 互不侵犯
在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。只有一行,包含兩個數n,k 1 n 9,0 k n n 方案數3 2 同sgu223 include include include include ...
SCOI2005 互不侵犯
題目描述 在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。輸入格式 只有一行,包含兩個數n,k 1 n 9,0 k n n 輸出格式 所得的方案數 ly最可愛啦 這題。想了5分鐘,寫了10分鐘,調了...
SCOI2005 互不侵犯
在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。兩個數n,k 1 n 9,0 k n n 方案數。3 2果然啊 狀壓題都是乙個套路 和前面那個noi的題是乙個套路 具體實現也基本一樣 就是記錄的狀態...