動態規劃模型總結之狀壓dp
啊怎麼前面的都沒寫就直接狀壓了啊
狀壓比較簡單先講狀壓(其實比較向深搜的優化)
可能藍書上的例題偏難先將一些簡單的
不會位運算就gg了吧
1.問題引入
為什麼需要狀壓dp
狀壓dp可以解決一些題目的狀態隨階段增長而增長的題目,比如一個量用了還是沒用等等,把狀態壓縮成數字存入陣列然後進行dp
啊聽著好抽象啊
那我們搞一個例題看看
簡化後的題目意思是有一塊矩陣,可以取一些1,但是不能有相鄰的1,問有多少種取法
此題是否可以 ?應該是可以的,但是會TLE
我們發現在 的是後需要記錄當前位置取沒取,這樣才能判斷該位置可不可以去
這提示我們可以使用狀壓
我們首先預處理出每一行有哪些取的方案合法,即不能取0也不能有相鄰
不能取0比較簡單,也就是把每行的草地情況壓成int(存到
那如何判斷一個狀態沒有相鄰的?這個就要考察位運算的功底了,其實就是左移一位和右移一位都與原數沒有交
預處理出這些就可以進入最後的 環節
表示第 行現在的狀態為
然後列舉之前的狀態為 ,如果 與 沒有交,則把 的值加到 裡面去
答案就是
我們發現如果第 行的取法只與 行和他自己有關,於是可以把 這一維滾動掉
時間複雜度
才12,正好
const int p = 100000000 ;
int dp[13][5000],a[13][13],f[13],ok[5000];
int n,m ;
int main(){
scanf("%d%d",&n,&m) ;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]) ;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
f[i]=(f[i]<<1)+a[i][j] ;
for (int i=0;i<(1<<m);i++) ok[i]=((!(i&(i<<1)))&&(!(i&(i>>1)))) ;
dp[0][0]=1 ;
for (int i=1;i<=n;i++){
for (int j=0;j<(1<<m);j++) {
if (ok[j] && ((j&f[i])==j)){
for (int k=0;k<(1<<m);k++)
if (!(j&k)){
dp[i][j]=(dp[i][j]+dp[i-1][k])%p ;
}
}
}
}
int ans=0;
for (int i=0;i<(1<<m);i++) ans=(ans+dp[n][i])%p ;
printf("%d\n",ans) ;
}
如果您覺得這個題目還比較困難,不妨先做一個小練習:
Water
題目描述
有 個裝著水的開水瓶,要把它們中的水彙總到不超過 個開水瓶裡,把第 個開水瓶裡的水全都倒到第 個開水瓶裡(無論它們現在裝了多少水)的代價是 。求最小總代價。
輸入格式
第一行兩個整數 。
接下來的 行,每行 個整數表示 ,保證 。
輸出格式
輸出一行一個整數表示答案。
樣例
input
5 2
0 5 4 3 2
7 0 4 4 4
3 3 0 1 2
4 3 1 0 5
4 5 5 5 0
output
5
資料範圍
。
如果您上一個題目聽懂了,這個就是小case了
表示瓶子狀態為 最小需要花費的代價
然後列舉把 號瓶子的水轉移到 號瓶子就好了
然後如果中間1的個數 的話就更新答案
int n, k, ans = iinf ;
int dp[N], a[25][25] ;
int calc(int x) {
int res = 0 ;
while (x) {
x = x & x - 1 ;
res++ ;
}
return res ;
}
signed main(){
scanf("%d%d", &n, &k) ;
if (n == k) print(0) ;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
scanf("%d", &a[i][j]) ;
for (int i = 0; i <= (1 << n) - 1; i++) dp[i] = iinf ;
dp[(1 << n) - 1] = 0 ;
for (int s = (1 << n) - 1; s >= 0; s--) { // 列舉當前狀態
if (calc(s) <= k) ans = min(ans, dp[s]) ;
for (int i = 0; i < n; i++) // 把第i個水壺中的水
if (s & (1 << i))
for (int j = 0; j < n; j++) // 轉移到第j個水壺中
if (i != j) {
dp[(s - (1 << i)) | (1 << j)] = min(dp[(s - (1 << i)) | (1 << j)], dp[s] + a[i][j]) ;
}
}
printf("%d\n", ans) ;
}