【HDU 1569 方格取数(2)】 网络流
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1569
题目大意:给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
解题思路:说实话,这样的网络流构图真的难想到,唉,练得太少了。
定理: 最大点独立集=sum-最小点覆盖集。
这里要我们求最大点独立集,由上面的定理我们就可以转换为求最小点覆盖集。我发现网上很多题解对于割这一块讲解的不知所云,可能是对割的理解还没深入吧,不会就看别人解题报告,然后自己按照自己的理解不知所云的讲。 这题可以用类似国际象棋黑白棋的方法解,每个点和它周围四个点连一条容量为无穷的边,源点和黑点(奇数点)相连,汇点和白点(偶数点)相连,剩下的就看你怎么割了,应该是这么割的:割掉最少最小的边,使得从源点的流量一点都不能到达汇点 ,等价于求源点到汇点的最大流。
建好图直接上模板,此刻顿时变成渣渣题了。
View Code
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int mn=3333; const int mm=1000000; const int oo=0x3fffffff; int node, st, sd, edge; int flow[mm], reach[mm], next[mm]; int head[mn], work[mn], dis[mn], que[mn]; void init(int node_, int st_, int sd_) { node=node_, st=st_, sd=sd_; for(int i=0; i<node; i++) head[i]=-1; edge=0; } void addedge(int u, int v, int c1, int c2) { reach[edge]=v, flow[edge]=c1, next[edge]=head[u], head[u]=edge++; reach[edge]=u, flow[edge]=c2, next[edge]=head[v], head[v]=edge++; } bool bfs() { int u, v, l=0, h=0; for(int i=0; i<node; i++) dis[i]=-1; que[l++]=st; dis[st]=0; while(l!=h) { u=que[h++]; if(h==mn) h=0; for(int i=head[u]; i>=0; i=next[i]) { v=reach[i]; if(flow[i]&&dis[v]<0) { dis[v]=dis[u]+1; que[l++]=v; if(l==mn) l=0; if(v==sd) return true; } } } return false; } int dfs(int u, int exp) { if(u==sd) return exp; for(int &i=work[u]; i>=0; i=next[i]) { int v=reach[i], tp; if(flow[i]&&dis[v]==dis[u]+1&&(tp=dfs(v,min(flow[i],exp))>0)) { flow[i]-=tp; flow[i^1]+=tp; return tp; } } return 0; } int Dinic() { int max_flow=0, flow; while(bfs()) { for(int i=0; i<node; i++) work[i]=head[i]; while(flow=dfs(st,oo)) max_flow+=flow; } return max_flow; } int main() { int m; while(cin >> m) { init(m*m+2,0,m*m+1); int sum=0, val; for(int i=1; i<=m; i++) for(int j=1; j<=m; j++) { scanf("%d",&val); sum+=val; if((i+j)&1) { addedge(st,(i-1)*m+j,val,0); if(i!=1) addedge((i-1)*m+j,(i-2)*m+j,oo,0); if(i!=m) addedge((i-1)*m+j,i*m+j,oo,0); if(j!=1) addedge((i-1)*m+j,(i-1)*m+j-1,oo,0); if(j!=m) addedge((i-1)*m+j,(i-1)*m+j+1,oo,0); } else addedge((i-1)*m+j,sd,val,0); } printf("%d\n",sum-Dinic()); } return 0; }