1. 程式人生 > 資訊 >礪算科技完成數億元天使輪融資,6nm GPU 明年上市,對話聯合創始人

礪算科技完成數億元天使輪融資,6nm GPU 明年上市,對話聯合創始人

狀壓DP

狀壓DP

定義:

OI-WIKI:狀壓 \(dp\) 是動態規劃的一種,通過將狀態壓縮為整數來達到優化轉移的目的。

其利用計算機二進位制的性質來描述狀態的一種DP方式。

很多棋盤問題都運用到了狀壓,同時,狀壓也很經常和BFS及DP連用。

前置

例題

P1896 [SCOI2005]互不侵犯

題目描述:在 \(n \times n(1 \le n \le 10)\) 的棋盤上放 \(k(0\le k<n\times n)\) 個國王,國王可攻擊相鄰的 \(8\) 個格子,求使它們無法互相攻擊的方案總數。

第一反應是什麼?搜尋?對不起你 T 了。想點複雜度可控的。

我們令每一行放國王為1,不放為0,則每一行都能用一個 01 串表示,而這個 01 串可以當成二進位制碼,把他轉化成十進位制,就對應了一個數,也就是說 \(n\le 9\) ,總共有 \([0,2^{9}]\) 種 01 串,其中不乏一些不合法的,我們可以預處理出合法的 01 串。再考慮01串的貢獻,明顯的1的個數就是貢獻,預處理的時候一併處理貢獻。

\(f[i][j][k]\) 表示前 \(i\) 行,十進位制表示下狀態為 \(j\) ,前 \(i\) 行放 \(k\) 個國王的方案數。顯然的轉移:

\[f[i][j][k]=\sum{f[i-1][x][k-sta[j]]} \]

啥意思?第 \(i\)

行的貢獻由 \(i-1\) 行累加,\(j\)\(x\) 分別代表兩種不同的合法狀態(行間合法,行內合法),放 \(k\) 個國王則由沒放 \(i\) 行時加上放 \(i\) 行的合法狀態的貢獻得來。

/*
Knowledge : Rubbish Algorithm
Work by : Dreamcatcher
Time : O(AC)
*/
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int Mod=1e9+7;
const int N=1e6+6;

int read() {
    int x=0,f=0;char ch=getchar(); 
    for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
    return f?-x:x;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+48);
}

int sta[N],sit[N],f[10][1005][90];
//狀態十進位制存sit,二進位制中1的個數存sta
//f[i][j][k] i行,j列舉狀態的十進位制,k統計國王個數 
int n,k,cnt;

void prepare(int x,int num,int cur){
    if(cur>=n) return sit[++cnt]=x,sta[cnt]=num,void();
    prepare(x,num,cur+1);prepare(x+(1<<cur),num+1,cur+2); 
} 
//預處理出可能存在的所有狀態 
 
bool Judge(int i,int j){
    if((sit[i]&sit[j])|((sit[i]<<1)&sit[j])|
    (sit[i]&(sit[j]<<1))) return 0;return 1;
}

signed main() {
   n=read();k=read();prepare(0,0,0);
   for(int i=1;i<=cnt;i++) f[1][i][sta[i]]=1;
   for(int i=2;i<=n;i++){
   for(int j=1;j<=cnt;j++){
   for(int x=1;x<=cnt;x++){
   if(!Judge(j,x)) continue;
   for(int l=sta[j];l<=k;l++) 
   f[i][j][l]+=f[i-1][x][l-sta[j]];
   }}}int Ans=0;for(int i=1;i<=cnt;i++) 
   Ans+=f[n][i][k];print(Ans);
   return 0;
}

其他題目以後再補。