礪算科技完成數億元天使輪融資,6nm GPU 明年上市,對話聯合創始人
阿新 • • 發佈:2022-02-23
狀壓DP
行的貢獻由 \(i-1\) 行累加,\(j\) 和 \(x\) 分別代表兩種不同的合法狀態(行間合法,行內合法),放 \(k\) 個國王則由沒放 \(i\) 行時加上放 \(i\) 行的合法狀態的貢獻得來。
狀壓DP
定義:
OI-WIKI
:狀壓 \(dp\) 是動態規劃的一種,通過將狀態壓縮為整數來達到優化轉移的目的。
其利用計算機二進位制的性質來描述狀態的一種DP方式。
很多棋盤問題都運用到了狀壓,同時,狀壓也很經常和BFS及DP連用。
前置
-
腦子一個
例題
題目描述:在 \(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\)
/* 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; }
其他題目以後再補。
水