狀壓DP入門

2022-05-11 16:15:42 字數 3028 閱讀 3696

首發於摸魚世界

狀壓dp,即狀態壓縮dp,它的精髓在於把dp過程中的乙個「狀態」用乙個二進位制數巧妙的表示出來。接下來就從一些入門的狀壓題目來感受一下狀壓的魅力吧~

洛谷p5911 [poi2004]prz

大致題意:

\(n\)個人過最大承載\(w\)重量的橋,每個人有重量\(w_i\)與過橋時間\(t_i\),多人一組時間取\(max\)。求最短過橋時間。

注意資料範圍:\(100≤w≤400,1≤t≤50,10≤w≤100,1≤n≤16\)

有沒有注意到\(n\)的資料範圍很耀眼。沒錯,這就是我們的突破口。

先想暴力記憶化搜尋,每個狀態記錄某些人過橋後的最小時間花費。為了記錄狀態,我們需要乙個\(bool\)陣列,甚至還可以瞎**回溯。

一通亂搞發現,盲目搜尋就是過不了

於是來優化吧!

每個狀態我們除了用乙個陣列記錄每個人是否加入,還有什麼辦法嗎?

狀!態!壓!縮!

因為人數比較少,我們可以用乙個\(n\)位的二進位制數來記錄乙個狀態。所以這個數轉成十進位制最大只有\(2^\)。可以存在陣列的一維中。

考慮用\(dp[i]\)表示狀態為\(i\)下的最少時間花費。它可以由什麼狀態推導得到呢?

假設當前狀態是\(0111\),我們可以把它拆成\(\,\\)另外兩個狀態。

那麼如何列舉呢?這就需要熟練掌握\(c++\)中的位運算子。很明顯,我們要先找到所有被狀態\(i\)包含的狀態\(j\),包含即\(j\)中所含的1在\(i\)中同乙個位置一定也是1,但反過來就不一定了。

比如\(1010\)可以延伸到:\(1010,1000,0010,0000\)四個狀態。

**怎麼實現呢?非常的amazing啊:for(int j=i;j>=0;j=(j-1)&i)

先給狀態\(j\)減去乙個1,再和\(i\)進行按位與,同時保證了不會漏枚和\(j\)被\(i\)包含。

那另外乙個狀態呢?進行按位異或就好了。比如上面的\(0111⊕0100=0011\),就可以把狀態\(i\)拆成\(j\)與\(i⊕j\)兩種了。

於是就有了:

\(dp[i]=min(dp[i],dp[j]+t[i⊕j])(w[i]<=w)\)

其中\(t[i]\)預處理為狀態\(i\)時的時間花費,\(w[i]\)預處理為狀態\(i\)時的重量和,初始值\(dp[i]=inf\)。

於是這道題就完成了。

單乙個一維表示狀態的題,應該還不算難

\(code:\)

#include#define inf 0x3f3f3f3f

using namespace std;

const int n=(1<<16)+1;

int dp[n],ti[n],we[n];

int t[17],c[17];

int main()

dp[i]=inf;

} dp[0]=0;

for(int i=0;i<1<=0;j=(j-1)&i)

printf("%d\n",dp[(1大致題意:

其中,\(1≤n,m≤20\)。

一道簡單的狀壓dp但是好像可能會mle。但是只是練習狀壓dp的話是一道好題。

考慮用\(dp[i][j]\)表示狀態\(i\)下,有\(j\)頭牛都選擇了合適位置的方案數。狀態儲存穀倉的選擇情況。

所以初值為\(dp[0][0]=1\)。列舉每一頭牛\(j\),在\(i\)狀態下如果他喜歡的穀倉\(v\)被占用了(\(i\&1<<(v-1)\)),那麼我們就\(dp[i][j]+=dp[i\hat{}1<<(v-1)][j-1]\),即把原有的那頭牛移開並把自己放進去,所以把原來的方案數累加上來。

最後\(ans=\sum_^ dp[i][n]\)。

\(code:\)

#includeusing namespace std;

int dp[1<<20][21];

int a[21];

int s[21][21];

int main()

dp[0][0]=1;

for(int j=1;j<=n;j++)

for(int i=1;i<1《洛谷 p1278 單詞遊戲

大致題意:

給定\(n(n≤16)\)個單詞,任意選擇其中單詞,要求為後乙個單詞的開頭必須與前乙個單詞的末尾字元相同。求最大能選擇的單詞總長度。

依然狀壓唄。

設\(dp[i][j]\)表示\(i\)狀態下以字元\(j\)結尾的最大單詞總長度。其中狀態\(i\)壓入了這\(n\)個單詞的選擇情況。

當然,首尾字元因為只有五個,我們一一對映到1-5或者0-4是沒有問題的,我為了方便,就直接把它們減去'a'作為標識就行了。記\(a[j],b[j]\)分別標識第\(j\)個單詞的首尾標識,\(l[j]\)記錄第\(j\)個單詞的長度,即對答案的貢獻。

再看轉移方程。如果當前第\(j\)個單詞沒有被選入列舉的狀態\(i\),那麼

\(dp[i+2^j][b[j]]=max\\)

其中初始值為\(dp[2^i][b[i]=l[i]\)

注意對於每乙個\(dp\)值,都去對\(ans\)取個\(max\)就好了。

\(code:\)

#includeusing namespace std;

const int n=20;

string x;

int a[n],b[n],l[n];

int f[1<<17][27];

int n;

int main()

for(int i=1;i<=n;i++)

for(int j=1;j<=n;j++)

for(int s=0;s<=1<<16;s++)

if(!(s&1

return 0;

}

狀壓DP入門

洛谷題號p1896 在n n的棋盤裡面放k個國王,使他們互不攻擊,共有多少種擺放方案。國王能攻擊到它上下左右,以及左上左下右上右下八個方向上附近的各乙個格仔,共8個格仔。題解 首先暴搜可肯定是超時的所以我門考慮狀壓 因為每個數都可以用二進位制表示出來 二進位制中01可以表示當前行放的棋子的位置 以及...

狀壓DP入門題

學習狀壓之前必須要熟練掌握位運算 位運算名 符號效果 and 按位與如果兩個相應的二進位制位都為1,則該位的結果值為1,否則為0 l or 按位或兩個相應的二進位制位中只要有乙個為1,該位的結果值為1 xor 按位異或 單身狗操作 若參加運算的兩個二進位制位值相同則為0,否則為1 取反 一元運算子,...

狀壓DP入門記錄

poj 3254 農場主john新買了一塊長方形的新牧場,這塊牧場被劃分成m行n列 1 m 12 1 n 12 每一格都是一塊正方形的土地。john打算在牧場上的某幾格里種上美味的草,供他的奶牛們享用。遺憾的是,有些土地相當貧瘠,不能用來種草。並且,奶牛們喜歡獨佔一塊草地的感覺,於是john不會選擇...