zoukankan      html  css  js  c++  java
  • HDU 1565

    题目链接: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
    ",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
    ",ans);
    48     }
    49 }

    PS.本题其实和POJ 1185:http://www.cnblogs.com/dilthey/p/7604432.html比较像,做法思路一脉相承,会做POJ1185的话,做这题应该不难。

  • 相关阅读:
    10.17T1 联通块
    10.16复习 数位DP——不要62
    10.16T6 逆序对变式
    10.16T5 最小环+拆点最短路
    10.16T4 GCD递归
    10.16T2 平方差
    10.16T3 乱搞+最优性剪枝
    10.16T1 二分+单调队列优化DP
    10.15T3 树形DP
    10.15T2 生成树+非树边暴力
  • 原文地址:https://www.cnblogs.com/dilthey/p/7623028.html
Copyright © 2011-2022 走看看