HDU 3376 拆点 网络流
2011-05-09 11:01
183 查看
http://acm.hdu.edu.cn/showproblem.php?pid=3376
这题是2686的加强版,用2686的方法照样可以解决,改成滚动数组就可以,另外也有网络流的解法:
拿第一个样例来说:
10 3
5 10
我们可以把每一个点看成两个映像,第一个看成 1与5,第二个 2与6,第三个 3与7,第四个 4与8,外加一个源点 0,汇点 9~
就变成了一个二分图:
1 5
2 6
0 3 7 9
4 8
接下来就是如何建图了:
1. 首先是源点,因为是从左上角开始,那么很好想,源点0与1连一条边,因为要两条路,那么源点过来的容量要为2,同理是汇点 9 与 8 连边,也是容量为2,两条边的费用都是0;
2. 其次是map[][]图如何对映到二分图上,就拿(1,1)这个点来说,首先它自己和自己的映像(1与5)之间是要建一条费用为map[1][1]即10的边,容量为1,代表(1,1)这个点走过了(走过了就得加上这条边的费用,将点与边对映上去);其次,(1,1)这个点它是可以走到(1,2)与(2,1)这两个点的:
模拟一下二分图,0走到1,1走到5,代表(1,1)这个点走过了,因为5到9是没有边的,
接下来为了能走到汇点9,
那么5应该回到二分图的左边,走到2或者3的位置,再由二分图的左边走向右边.....可以好好想下这个过程!所以得建5到2,和5到3的边(2,与3对映的是(1,2),(2,1)两点),这条边的容量为1,费用要为0,因为从(1,1)这个点走过来的,加上的只是(1,1)这个点的费用,这在1走到5的时候已经加过了!!要从2,3走到右边,走的时候自然会加上,所以费用为0,不然会导致重复加!
建边真的很奇妙,也就难在这里了,建好图之后就很简单,还是得多做题,接触多一点的建边模型!
#include<iostream> #include<queue> #define N 605*605*2 #define M 605*605*10 using namespace std; int beg,end; struct Graph { struct node { int v,next,w,flow; node(){}; node(int a,int b,int c,int d){ next=a;v=b;w=c;flow=d; } }E[M]; int head ; bool h ; int path ; int dis ; int NV,NE; int pre ; void init(int n) { NV=n; NE=0; memset(head,-1,sizeof(head)); } void insert(int u,int v,int w,int flow) { E[NE]=node(head[u],v,w,flow); head[u]=NE++; E[NE]=node(head[v],u,-w,0); head[v]=NE++; } bool update(int u,int v,int w) { if(dis[u]+w>dis[v]) { dis[v]=dis[u]+w; return true; } return false; } bool spfa() { memset(h,0,sizeof(h)); memset(pre,-1,sizeof(pre)); memset(dis,-1,sizeof(dis)); dis[beg]=0; queue<int> q; q.push(beg); while(!q.empty()) { int u=q.front(); q.pop(); h[u]=0; for(int i=head[u];i!=-1;i=E[i].next) { int v=E[i].v; if(E[i].flow>0&&update(u,v,E[i].w)) { pre[v]=u; path[v]=i; if(!h[v]) { h[v]=1; q.push(v); } } } } if(pre[end]==-1) return false; return true; } int maxcost_maxflow() { int ans=0; while(spfa()) { int Min=1; for(int i=end;i!=beg;i=pre[i]) { E[path[i]].flow-=Min; E[path[i]^1].flow+=Min; } ans+=dis[end]; } return ans; } }G; int main(void) { int n; while(scanf("%d",&n)!=EOF) { beg=0; end=2*n*n+1; G.init(end); G.insert(beg,1,0,2); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int x; scanf("%d",&x); int u=(i-1)*n+j; int v=u+n*n; G.insert(u,v,x,1); G.insert(u,v,0,1); if(i<n) G.insert(v,u+n,0,1); if(j<n) G.insert(v,u+1,0,1); } G.insert(2*n*n,end,0,2); printf("%d/n",G.maxcost_maxflow()); } }
这题是2686的加强版,用2686的方法照样可以解决,改成滚动数组就可以,另外也有网络流的解法:
拿第一个样例来说:
10 3
5 10
我们可以把每一个点看成两个映像,第一个看成 1与5,第二个 2与6,第三个 3与7,第四个 4与8,外加一个源点 0,汇点 9~
就变成了一个二分图:
1 5
2 6
0 3 7 9
4 8
接下来就是如何建图了:
1. 首先是源点,因为是从左上角开始,那么很好想,源点0与1连一条边,因为要两条路,那么源点过来的容量要为2,同理是汇点 9 与 8 连边,也是容量为2,两条边的费用都是0;
2. 其次是map[][]图如何对映到二分图上,就拿(1,1)这个点来说,首先它自己和自己的映像(1与5)之间是要建一条费用为map[1][1]即10的边,容量为1,代表(1,1)这个点走过了(走过了就得加上这条边的费用,将点与边对映上去);其次,(1,1)这个点它是可以走到(1,2)与(2,1)这两个点的:
模拟一下二分图,0走到1,1走到5,代表(1,1)这个点走过了,因为5到9是没有边的,
接下来为了能走到汇点9,
那么5应该回到二分图的左边,走到2或者3的位置,再由二分图的左边走向右边.....可以好好想下这个过程!所以得建5到2,和5到3的边(2,与3对映的是(1,2),(2,1)两点),这条边的容量为1,费用要为0,因为从(1,1)这个点走过来的,加上的只是(1,1)这个点的费用,这在1走到5的时候已经加过了!!要从2,3走到右边,走的时候自然会加上,所以费用为0,不然会导致重复加!
建边真的很奇妙,也就难在这里了,建好图之后就很简单,还是得多做题,接触多一点的建边模型!
相关文章推荐
- 【HDU 3376 Matrix Again】网络流 & 拆点 & 最大费用最大流
- HDU 2686 && HDU 3376(网络流之费用流)
- hdu 3416 最短路+网络流(不重叠最短路径计数)
- [2016ICPC 青岛网络预选赛] HDU 5889 网络流
- HDU 4240 Route Redundancy 网络流
- HDU 3472 HS BDC 混合图欧拉回路 网络流
- HDU 3277 Marriage Match III 网络流(dinic 优化)+floyd
- 网络流 (费用流+拆边)——Transportation ( HDU 3667 )
- HDU 3472 HS BDC 混合图的欧拉路径判断 (网络流)
- HDU 4940 Destroy Transportation system 无源汇有上下界的网络流
- hdu 3549 Flow Problem 基本网络流。
- hdu 1815 网络流寻找字典序最小割集 好题
- hdu 3572 Task Schedule (网络流)
- HDU 4971 A simple brute force(网络流)
- HDU 4309 Seikimatsu Occult Tonneru 网络流+状压
- HDU 3549 Flow Problem(有向边网络流)
- HDU-1565 方格取数(1) 网络流
- HDU1532——Drainage Ditches(网络流Dinic算法)
- HDU 3468 Treasure Hunting(BFS+网络流之最大流)
- hdu 4240 Route Redundancy 网络流