HDU1565:方格取數(1) (狀態壓縮DP)
阿新 • • 發佈:2019-01-02
Problem Description
給你一個n*n的格子的棋盤,每個格子裡面有一個非負數。
從中取出若干個數,使得任意的兩個數所在的格子沒有公共邊,就是說所取的數所在的2個格子不能相鄰,並且取出的數的和最大。
Input
包括多個測試例項,每個測試例項包括一個整數n 和n*n個非負數(n<=20)
Output
對於每個測試例項,輸出可能取得的最大的和
Sample Input
3
75 15 21
75 15 28
34 70 5
Sample Output
188
與HDU1074有點相似,還是用二進位制來記錄表示取或者不取,一行行的進行計算,用一個數組記錄上一行的所有取法,一個數組記錄現在這行的所有取法,如果取法進行與運算為1的話,那麼就代表有相鄰的而不進行計算,然後這樣一直DP到最後一行便可以得到最後的結果
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int L = 20000; int n,a[20][20]; int dp[L],tem[L]; int now[L],pre[L]; int ans[L],pre_size,now_size; void dfs(int id,int k,int p,int sum) { if(k>=n)//超過n則可以記錄這個狀態 { now[++now_size] = p; ans[now_size] = sum; return ; } dfs(id,k+2,p|(1<<k),sum+a[id][k]);//這個位置取了,那麼就要加上這個位的二進位制,通過或運算得出,這個位置取了的話,那麼下一個要取的至少要跳兩格 dfs(id,k+1,p,sum);//這個位置不取並跳一格 } void DP() { int i,j,k; for(k = 1; k<=n; k++) { now_size = 0; dfs(k,0,0,0);//搜出此行的所有狀態 for(i = 1; i<=now_size; i++) dp[i] = 0; for(i = 1; i<=now_size; i++) { for(j = 1; j<=pre_size; j++) { if(now[i]&pre[j]) continue;//相與為1,證明有相鄰而不繼續往下 dp[i] = max(dp[i],tem[j]+ans[i]); } } for(i = 1; i<=now_size; i++)//目前這行的狀態儲存為上一行 { tem[i] = dp[i]; pre[i] = now[i]; } pre_size = now_size; } } int main() { int i,j; while(~scanf("%d",&n)) { for(i = 1; i<=n; i++) for(j = 0; j<n; j++) scanf("%d",&a[i][j]); tem[1] = pre[1] = 0;//先取一個,但是位置並不確定, pre_size = 1; DP(); int ans = 0; for(i = 1;i<=pre_size;i++) ans = max(ans,tem[i]); printf("%d\n",ans); } return 0; }