洛谷 P1896 [SCOI2005]互不侵犯 狀壓dp

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

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

輸入格式

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

輸出格式

所得的方案數

輸入輸出樣例

輸入 #1複製

3 2

輸出 #1複製

16

題面很簡單,但是這個題不能像n皇后那樣去爆搜,我們的皇后問題可以爆搜是應爲一個皇后放好後,我們就不會再考慮當前行了和當前列了,直接到下一行進行操作。這個國王問題一行可以放多個國王,一列也可以放多個。

題目的範圍很小,可以採用狀壓dp。(1 << 9) = 512,就算是開三維也開的下。

先來分析題目,這題很類似擺放瓷磚那道題(poj 2411 沒做過的小夥伴可以先嚐試),也只用考慮上一行對當前行的影響。

由於國王對下一行的影響只有下面的三個格子。所以我們可以移位後按位 &。

例:第i行的擺放情況:k=0100    i+1:k1=0100

當前  k&k1 = 1

k1 = 1000 時 k&k1 = 1。 k1 = 0010 時同理。那麼我們發現了只要 k&k1 ,k & (k1>>1),k & (k1 << 1)。這三個情況都爲0,那麼當前位置是可放國王的

詳情見代碼註釋

#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <bitset>
#define  LL long long
#define  ULL unsigned long long
#define mod 10007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
//#define maxn 50
using namespace std;
const int maxn = (1 << 9) + 5;
int n,m;
LL dp[maxn][maxn][maxn];
int vis[maxn];
int num[maxn];
//初始化第一行的dp
void init()
{
    for(int i = 0; i < (1 << n); i++){
        if(!(i & (i >> 1))){//都在第一行放國王,只要滿足不相鄰即可
           int k = i;
           while(k){
            num[i] += (k & 1);//算出在i狀態下襬了多少個國王
            k >>= 1;
           }
           vis[i] = 1;//這裏的vis數組是必要的,記錄是每一行的所有可行解
           dp[1][num[i]][i] = 1;
        }
    }
}
void solve()
{
    int p = (1 << n);
    for(int i = 1; i <= n; i++){//從第一行開始遍歷到第n行
      for(int j = 0; j < p; j++){//上一行的狀態
        if(vis[j])
         for(int k = 0; k < p; k++){//本行的狀態
           if(vis[k])
            if(!(j & k) && (!(j & (k >> 1))) && (!(j & (k << 1)))){//滿足條件
             for(int temp = num[j];num[k] + temp <= m; temp++){//嘗試多放幾個國王,k狀態的下的當前行的
                dp[i + 1][temp + num[k]][k] += dp[i][temp][j];
             }
           }
        }
      }
    }
    LL ans = 0;
    for(int i = 0; i < p; i++) ans += dp[n][m][i];
    printf("%lld\n",ans);
}
int main()
{
    scanf("%d%d",&n,&m);
    init();
    solve();


    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章