橫版動作遊戲《密林傳奇》今日發售 支援中文
阿新 • • 發佈:2021-11-03
我們需要選擇5個數,對於每一個數,不選就為0,選就為1
那麼對於5個數,我們就可以用5位的二進位制數來表示當前的狀態
這種將集合作為整數記錄狀態的一類演算法叫做狀態壓縮DP
在狀態壓縮中,二位用二進位制儲存,所以有一些常用的操作:
(1)空集φ:0
(2)只含有第i個元素的集合{i}:1<<i
(3)含有全部n個元素的集合{0,1,...,n-1}:(1<<n)-1
(4)判斷第i個元素是否屬於集合S:if(S>>i&1)
(5)向集合S中加入第i個元素S∪{i}:S|1<<i
(6)從集合S中移出第i個元素S{i}:S&~(1<<i)
(7)集合S和T的並集S∪T:S|T
(8)集合S和T的交集S∩T:S&T
此外,想要將集合S={0,1,...,n-1}表示的所有狀態枚舉出來,可以寫成:
for(int s=0;s<(1<<n);s++){
//對子集的處理
}
例題
在這道題中,可以看到單詞數目很少,容易想到列舉所有的單詞連線情況,在判斷最大長度,但情況太多(\(n!\)),會超時
所以用狀態壓縮DP
\(f[i][j]\)表示已經連線到了單詞I,此時串的狀態為S
狀態用狀態壓縮,變成二進位制
狀態轉移方程:
\(f[i][j+s]=max(f[i][j]+length[s])\)
最大狀態為1111111111111111,轉換成二進位制就為65535,再加上1
狀態壓縮陣列就是:
int f[16][65536];
常數陣列:(只包含第i個單詞)
const int only[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
邊界條件說明:狀態中只有一個單詞的情況:\(f[i][only[i]]=len[i]\);
條件判斷
if(only[i]&j){
//表示 “ 如果單詞i在狀態j中…”(注意:其實這裡的i指的是第i+1個單詞,下面一樣,因為編號從0開始)
}
總程式碼:
#include<bits/stdc++.h> using namespace std; const int only[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768}; int n; char s[16][102]; int f[16][65536]; int len[16]; int work(int i,int j){ int j1,temp; if(f[i][j]>0) return f[i][j]; j1=j-only[i];//從狀態j中刪去i for(int k=0;k<n;k++) if(i!=k&&s[i][len[i]-1]==s[k][0]&&(only[k]&j)){//可以接龍 temp=work(k,j1)+len[i]; f[i][j]=max(f[i][j],temp); } return f[i][j]; } int main(){ cin>>n; for(int i=0;i<n;i++){ scanf("%s",s[i]); len[i]=strlen(s[i]); } for(int i=0;i<n;i++) f[i][only[i]]=len[i]; int ans,temp; ans=0; for(int i=0;i<n;i++)\\從第i個單詞開始連 for(int j=1;j<(1<<n);j++) if((only[i]&j)){ temp=work(i,j); ans=max(ans,temp); } cout<<ans<<endl; return 0; }
謝謝觀看
PS.