洛谷P1896 [SCOI2005]互不侵犯 狀壓dp+位運算

題目鏈接:https://www.luogu.org/problem/P1896

題意:n*n的格子填數,每個數填放位置的周圍(8個)不能有其他的數

n<=9 ,矩形狀壓 f[i][j][s],f[i][j][s]就表示在只考慮前i行時,在前i行(包括第i行)有且僅有s個國王,且第i行國王的情況是編號爲j的狀態時情況的總數。

狀態轉移方程 f[i][j][[s]=sum(f[i-1][k][s-gs[j]]) 而k就代表第i-1行的國王情況的狀態編號

用位運算預處理滿足的狀態 ,假設該狀態爲j,j的二進制不能有兩個相鄰的1,(j<<1&j)!=1即可判斷

sit[j]表示第j種狀態是啥,gs[j]表示第j種狀態的個數

判斷兩行是否滿足條件,比如第一行狀態j,第二行狀態k:

上下不能有相鄰的數 (j&k)!=1

第二行某個數左上或者右上不能有數 (j<<1&k)!=1  (j>>1&k)!=1

代碼:


//#pragma comment (linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#include<list>
#include<time.h>
#include<bitset>

#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define lowbit(x) x&(-x)
#define min4(a, b, c, d) min(min(a,b),min(c,d))
#define min3(x, y, z) min(min(x,y),z)
#define max3(x, y, z) max(max(x,y),z)
#define max4(a, b, c, d) max(max(a,b),max(c,d))
#define pii make_pair
#define pr pair<int,int>
typedef unsigned long long ull;
typedef long long ll;
const int inff = 0x3f3f3f3f;
const long long inFF = 9223372036854775807;
const int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
const int mdir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 1, -1, -1, -1};
const double eps = 1e-10;
const double PI = acos(-1.0);
const double E = 2.718281828459;
using namespace std;
const int mod=1e9+7;
const int maxn=2005;
ll f[10][2000][100]={0};//行數,改行狀態,個數
int sit[2005],gs[2005];//sit表示可用狀態,gs表示個數
int n,m;
int cnt=0;
int getsum(int x)
{
    int ans=0;
    while(x) ans+=(x&1),x>>=1;
    return ans;
}
void init()//預處理
{
    for(int i=0;i<(1<<n);i++)
        if(!(i<<1&i)) sit[++cnt]=i,gs[cnt]=getsum(i);
}
int main()
{
    cin>>n>>m;
    init();
    for(int i=1;i<=cnt;i++) f[1][i][gs[i]]=1;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=cnt;j++)
            for(int k=1;k<=cnt;k++)//枚舉i,j,k,f[i][j][s]=f[i-1][k][s-gs[j]]
            {
                if(sit[j]&sit[k]) continue;
                if(sit[j]<<1&sit[k]) continue;
                if(sit[j]>>1&sit[k]) continue;
                for(int s=m;s>=gs[j];s--) f[i][j][s]+=f[i-1][k][s-gs[j]];
            }
    ll ans=0;
    for(int i=1;i<=cnt;i++) ans+=f[n][i][m];
    cout<<ans<<endl;
}

 

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