HDU 1565 方格取数(1)(最大点权独立集)

http://acm.hdu.edu.cn/showproblem.php?pid=1565

题意:

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。 
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。

 

思路:

最大点权独立集=点权之和-最小点权覆盖集=最小割=最大流

先来看最小点权覆盖集,也就是选取点覆盖所有的边,并且权值要最小。

解决方法是:

从源点向X集连边,容量为点的权值,Y集向汇点连边,容量也为点的权值。如果u和v这两个点相连的话,则将这两个点连一条有向边,容量为INF,因为我们要割的不是这个。这样,从s到t的路径中,就包含了所有的边,最小点覆盖也就是连通所有边,最小割就是让所有边都不连通,于是求个最大流即可。

这样一来,最大点权独立集也就可以求出来了。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<queue>
  6 using namespace std;
  7 typedef long long LL;
  8 
  9 const int maxn=2000+5;
 10 const int INF=0x3f3f3f3f;
 11 
 12 struct Edge
 13 {
 14     int from,to,cap,flow;
 15     Edge(int u,int v,int w,int f):from(u),to(v),cap(w),flow(f){}
 16 };
 17 
 18 struct Dinic
 19 {
 20     int n,m,s,t;
 21     vector<Edge> edges;
 22     vector<int> G[maxn];
 23     bool vis[maxn];
 24     int cur[maxn];
 25     int d[maxn];
 26 
 27     void init(int n)
 28     {
 29         this->n=n;
 30         for(int i=0;i<n;++i) G[i].clear();
 31         edges.clear();
 32     }
 33 
 34     void AddEdge(int from,int to,int cap)
 35     {
 36         edges.push_back( Edge(from,to,cap,0) );
 37         edges.push_back( Edge(to,from,0,0) );
 38         m=edges.size();
 39         G[from].push_back(m-2);
 40         G[to].push_back(m-1);
 41     }
 42 
 43     bool BFS()
 44     {
 45         queue<int> Q;
 46         memset(vis,0,sizeof(vis));
 47         vis[s]=true;
 48         d[s]=0;
 49         Q.push(s);
 50         while(!Q.empty())
 51         {
 52             int x=Q.front(); Q.pop();
 53             for(int i=0;i<G[x].size();++i)
 54             {
 55                 Edge& e=edges[G[x][i]];
 56                 if(!vis[e.to] && e.cap>e.flow)
 57                 {
 58                     vis[e.to]=true;
 59                     d[e.to]=d[x]+1;
 60                     Q.push(e.to);
 61                 }
 62             }
 63         }
 64         return vis[t];
 65     }
 66 
 67     int DFS(int x,int a)
 68     {
 69         if(x==t || a==0) return a;
 70         int flow=0, f;
 71         for(int &i=cur[x];i<G[x].size();++i)
 72         {
 73             Edge &e=edges[G[x][i]];
 74             if(d[e.to]==d[x]+1 && (f=DFS(e.to,min(a,e.cap-e.flow) ) )>0)
 75             {
 76                 e.flow +=f;
 77                 edges[G[x][i]^1].flow -=f;
 78                 flow +=f;
 79                 a -=f;
 80                 if(a==0) break;
 81             }
 82         }
 83         return flow;
 84     }
 85 
 86     int Maxflow(int s,int t)
 87     {
 88         this->s=s; this->t=t;
 89         int flow=0;
 90         while(BFS())
 91         {
 92             memset(cur,0,sizeof(cur));
 93             flow +=DFS(s,INF);
 94         }
 95         return flow;
 96     }
 97 }DC;
 98 
 99 int n;
100 int map[25][25];
101 int dx[]={0,0,1,-1};
102 int dy[]={1,-1,0,0};
103 
104 int main()
105 {
106     while(~scanf("%d",&n))
107     {
108         int sum=0;
109         int src=0,dst=n*n+1;
110         DC.init(dst+1);
111         for(int i=1;i<=n;i++)
112         for(int j=1;j<=n;j++)
113         {
114             scanf("%d",&map[i][j]);
115             sum+=map[i][j];
116         }
117         for(int i=1;i<=n;i++)
118             for(int j=1;j<=n;j++)
119             {
120                 int id=(i-1)*n+j;
121                 int t=(i+j)%2;
122                 if(t)
123                 {
124                    DC.AddEdge(src,id,map[i][j]);
125                    for(int k=0;k<4;k++)
126                    {
127                       int x=dx[k]+i;
128                       int y=dy[k]+j;
129                       if(x<1||x>n||y<1||y>n)  continue;
130                       DC.AddEdge(id,(x-1)*n+y,INF);
131                    }
132                 }
133                 else DC.AddEdge(id,dst,map[i][j]);
134             }
135         int ans=DC.Maxflow(src,dst);
136         printf("%d\n",sum-ans);
137     }
138     return 0;
139 }

 

转载于:https://www.cnblogs.com/zyb993963526/p/6698631.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值