1. 程式人生 > >HDU 1565 - 方格取數(1) - [狀壓DP][網絡流 - 最大點權獨立集和最小點權覆蓋集]

HDU 1565 - 方格取數(1) - [狀壓DP][網絡流 - 最大點權獨立集和最小點權覆蓋集]

printf 一個 cnblogs ret com bool limit .net amp

題目鏈接:https://cn.vjudge.net/problem/HDU-1565

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

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

看了一下題目,立刻就想起來曾經好像在網絡流專題裏做過類似題目,

一翻博客,果然找到了這題的brother:http://www.cnblogs.com/dilthey/p/7401563.html

題解:

解法①:

既然,連這題的加強版 HDU 1569 - 方格取數 都可以用網絡流做,這題當然也可以,代碼幾乎就是一點點修改:

技術分享
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<queue>
  5 #define MAX 405
  6 #define
INF 0x3f3f3f3f 7 #define id(i,j) (i-1)*n+j 8 using namespace std; 9 struct Edge{ 10 int u,v,c,f; 11 }; 12 struct Dinic 13 { 14 int s,t; 15 vector<Edge> E; 16 vector<int> G[MAX]; 17 bool vis[MAX]; 18 int lev[MAX]; 19 int cur[MAX]; 20 void init(int
l,int r) 21 { 22 E.clear(); 23 for(int i=l;i<=r;i++) G[i].clear(); 24 } 25 void addedge(int from,int to,int cap) 26 { 27 E.push_back((Edge){from,to,cap,0}); 28 E.push_back((Edge){to,from,0,0}); 29 int m=E.size(); 30 G[from].push_back(m-2); 31 G[to].push_back(m-1); 32 } 33 bool bfs() 34 { 35 memset(vis,0,sizeof(vis)); 36 queue<int> q; 37 q.push(s); 38 lev[s]=0; 39 vis[s]=1; 40 while(!q.empty()) 41 { 42 int now=q.front(); q.pop(); 43 for(int i=0,_size=G[now].size();i<_size;i++) 44 { 45 Edge edge=E[G[now][i]]; 46 int nex=edge.v; 47 if(!vis[nex] && edge.c>edge.f) 48 { 49 lev[nex]=lev[now]+1; 50 q.push(nex); 51 vis[nex]=1; 52 } 53 } 54 } 55 return vis[t]; 56 } 57 int dfs(int now,int aug) 58 { 59 if(now==t || aug==0) return aug; 60 int flow=0,f; 61 for(int& i=cur[now],_size=G[now].size();i<_size;i++) 62 { 63 Edge& edge=E[G[now][i]]; 64 int nex=edge.v; 65 if(lev[now]+1 == lev[nex] && (f=dfs(nex,min(aug,edge.c-edge.f)))>0) 66 { 67 edge.f+=f; 68 E[G[now][i]^1].f-=f; 69 flow+=f; 70 aug-=f; 71 if(!aug) break; 72 } 73 } 74 return flow; 75 } 76 int maxflow() 77 { 78 int flow=0; 79 while(bfs()) 80 { 81 memset(cur,0,sizeof(cur)); 82 flow+=dfs(s,INF); 83 } 84 return flow; 85 } 86 }dinic; 87 int n,grid[23][23],sum; 88 int d[4][2]={{0,+1},{+1,0},{0,-1},{-1,0}}; 89 bool inmap(int i,int j){return( 1<=i && i<=n && 1<=j && j<=n );} 90 int main() 91 { 92 while(scanf("%d",&n)!=EOF) 93 { 94 dinic.init(0,n*n+1); 95 dinic.s=0, dinic.t=n*n+1; 96 sum=0; 97 for(int i=1;i<=n;i++) 98 { 99 for(int j=1;j<=n;j++) 100 { 101 scanf("%d",&grid[i][j]); 102 sum+=grid[i][j]; 103 if((i+j)%2) 104 { 105 for(int k=0;k<4;k++) if(inmap(i+d[k][0],j+d[k][1])) dinic.addedge(id(i,j), id(i+d[k][0],j+d[k][1]), INF); 106 dinic.addedge(dinic.s, id(i,j), grid[i][j]); 107 } 108 else dinic.addedge(id(i,j), dinic.t, grid[i][j]); 109 } 110 } 111 printf("%d\n",sum-dinic.maxflow()); 112 } 113 }
View Code

解法②:

當然,既然放在狀壓DP專題,當然也說明了這種題目,在列數在比較小的情況下,可以用狀壓DP做(HDU1569因為列數達到50,所以不行);

首先,由於1<<20的狀態數,還是偏大了點,依然選擇類似於http://www.cnblogs.com/dilthey/p/7604432.html中篩選狀態的操作,得到最多的狀態數為17711;

然後,設dp[r][i]:表示第r行為狀態i時(顯然這個i在二進制表示下,每一位上,0表示不取對應位置的數,1表示取),從第一行到第r行能取到的最大的和;

而要得到dp[r][i],只需要把dp[r-1][j]枚舉一遍,做相應的一定的計算,再保證維護dp[r][i]始終為最大即可,這就是狀態轉移;

其中所謂“做相應的一定的計算”,需要一個calc(r, state)函數:

  calc(r, state)返回值為:第r行當狀態為state時,取到的數之和;

AC代碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 int n,grid[23][23],ans;
 6 int dp[23][17720];
 7 int state[17720],state_cnt;
 8 int calc(int r,int state)
 9 {
10     int ans=0;
11     int cnt=n;
12     while(state)
13     {
14         if(state&1) ans+=grid[r][cnt];
15         state=state>>1;
16         cnt--;
17     }
18     return ans;
19 }
20 int main()
21 {
22     while(scanf("%d",&n)!=EOF)
23     {
24         memset(dp,0,sizeof(dp));
25         for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&grid[i][j]);
26         state_cnt=0;
27         for(int i=0;i<(1<<n);i++)
28         {
29             if(i&(i<<1)) continue;
30             dp[1][state_cnt]=calc(1,i);
31             state[state_cnt++]=i;
32         }
33         for(int r=2;r<=n;r++)
34         {
35             for(int i=0;i<state_cnt;i++)//枚舉第r行狀態
36             {
37                 for(int j=0;j<state_cnt;j++)//枚舉第r-1行狀態
38                 {
39                     if(state[i]&state[j]) continue;
40                     dp[r][i]=max(dp[r][i],dp[r-1][j]+calc(r,state[i]));
41                 }
42             }
43         }
44 
45         ans=0;
46         for(int i=0;i<state_cnt;i++) ans=max(ans,dp[n][i]);
47         printf("%d\n",ans);
48     }
49 }

PS.本題其實和POJ 1185:http://www.cnblogs.com/dilthey/p/7604432.html比較像,做法思路一脈相承,會做POJ1185的話,做這題應該不難。

HDU 1565 - 方格取數(1) - [狀壓DP][網絡流 - 最大點權獨立集和最小點權覆蓋集]