【題解】洛谷P1896 [SCOI2005] 互不侵犯(狀壓DP)
阿新 • • 發佈:2018-11-11
洛谷P1896:https://www.luogu.org/problemnew/show/P1896
前言
這是一道狀壓DP的經典題
原來已經做過了 但是快要NOIP 複習一波
關於一些位運算的知識點參考:
https://blog.csdn.net/fox64194167/article/details/20692645
思路
看資料識算法系列
我們用f[i][j][k]來表示第i行為狀態j 並且前i行已經放了k個國王
對於狀態我們可以先預處理出來
因為每個格子有放和不放兩種選擇
那麼我們可以想到轉化為二進位制來區分他們的狀態
如果有放為1 沒放為0
因此狀態最多可以達到2n種(每個格子都放)
所以我們預處理出所有的狀態 之後在進行DP詳細的判斷即可
程式碼
#include<iostream> using namespace std; #define ll long long ll f[20][2000][155]; ll ans; int num[2000],s[2000],n,k,cnt;//num為每種狀態可以防止的國王數 //s為狀態 void pre() { for(int i=0;i<(1<<n);i++)//列舉所有狀態 {if(i&(i<<1)) continue;//如果衝突了 就跳過 //這裡可以看成同一行裡連著放了2個不滿足 int sum=0;//國王數 for(int j=0;j<n;j++)//統計此狀態放置的國王的個數 if(i&(1<<j)) sum++;//有放則為1 s[++cnt]=i;//新增狀態 num[cnt]=sum;//統計國王數 } } void dp() { f[0][1][0]=1;//初始化 for(int i=1;i<=n;i++)//列舉行 for(int j=1;j<=cnt;j++)//列舉此行狀態 for(int sum=0;sum<=k;sum++)//列舉前i行的國王數 { if(sum>=num[j])//如果前i行的國王數大於這種狀態要放的國王數 //說明可以用這種狀態 { for(int t=1;t<=cnt;t++)//列舉第i-1行的狀態 { if(!(s[t]&s[j])&&!(s[t]&(s[j]<<1))&&!(s[t]&(s[j]>>1))) //無衝突 f[i][j][sum]+=f[i-1][t][sum-num[j]];//加上之前的方案 } } } for(int i=1;i<=cnt;i++) ans+=f[n][i][k];//ans為第n行已經放完所有國王的所有狀態的累計 cout<<ans; } int main() { cin>>n>>k; pre();//預處理 dp(); }