SCOI2005 互不侵犯(狀壓DP)

2021-09-26 04:24:28 字數 1127 閱讀 8418

題意:給定乙個n * n的棋盤,放置k個國王,使他們互相攻擊不到對方,共有多少種方案。國王可以攻擊上下左右,左上左下,右上右下附近的一格,共8格。

資料範圍:1<=n<=9, 0<=k<=n * n.

因為n的範圍很小,而且每一行對應的乙個方案可以用乙個二進位制數表示,所以容易想到用狀壓dp。又因題目有限制要用多少個國王,所以我們需要記錄一下國王的個數。

於是,我們可以定義dp狀態:

dp[i,j,k]表示第i行採用j方案,國王總個數為k時的總方案數。

那麼如何推出狀態轉移方程呢?

狀壓dp在於列舉,我們先想想怎麼列舉。

設定can[i],記錄在不考慮行與行之間影響的情況下,每行可以採取的方案。那麼,因為每個方案對應有用到一定個數的國王,所以再設定乙個num[i],記錄採用can[i]方案時用了多少個國王。

初始完can[i],num[i]後,首先初始化第一行:dp[1,can[i],num[i]]=1.

對於i ← 2 ~ n行,假如i-1行採用了can[j]方案,i行採用了can[k]方案,那麼在can[k]與can[j]不衝突的情況下,不難推出:dp[i,can[k],num[k]+p]+=dp[i,can[j],p],其中0<=p<=k-num[k],即表示第i行採用can[k]方案時在前一行採用can[j]方案,且國王總個數為p的情況下推出來的。

**如下:

#include#include#include#includeusing namespace std;

typedef long long ll;

const int maxn=1<<9;

ll dp[10][maxn][90];

int can[maxn],num[maxn];

int count(int x)

return sum;

}int main()

}} ll sum=0;

for(int i=1;i<=cnt;i++) //最終答案落在sum(dp[n,can[1~cnt],k])

sum+=dp[n][can[i]][k];

cout<}

SCOI2005 互不侵犯(狀壓DP)

在 n n 的棋盤裡面放 k 個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。一行 n,k 方案數狀態壓縮dp基礎 狀壓和二進位制有著不可割捨的聯絡 一幫情況下,我 習慣把狀態抽象成一維 並且用二進位制表示,然後再去想怎...

SCOI2005 互不侵犯 (狀壓DP)

題目鏈結 在 n times n 的棋盤裡面放 k 個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共 8 個格仔。1 le n le 9,0 le k le n n f i,j,l 來表示前 i 行,當前狀態為 j 且已經放置 l...

SCOI2005 互不侵犯King (狀壓DP)

給乙個n n n 9 的棋盤,問放k k n n 個國王滿足不衝突的方案數,國王的攻擊範圍是周圍的八個位置。非常經典的狀壓dp,dp i j s 代表第i行,已經存了j個棋子,這一行的國王存放狀態為s的方案數,空間複雜度n k 2 n 大概1e6,時間複雜度n k 2 2n 大概2e8,最後在dar...