poj 2315 最小费用最大流入门题
2015-07-31 23:52
507 查看
增加一个超级源点和超级汇点,容量为2,费用为0。
有重边,且为无向边。所以要用邻接表,需要建4条边。
邻接表的实现也可以在通过在Edge结构体中加入next变量,保存的是和它邻接的theEdge的下标。head[]数组保存的是某点最后一次出现在theEdge的下标。
例如,对于输入数据
有重边,且为无向边。所以要用邻接表,需要建4条边。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<string> #include<map> #include<set> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<sstream> #define LL long long #define OJ_PRINT 0 #define READ_FILE 0 using namespace std; const int NN_MAX = 1100; const int MM_MAX = 10000+10; const int INF = 0x1fffffff; struct Edge{ int from,to,cap,flow,cost; Edge (int a1=0,int b1=0,int c1=0,int d1=0,int e1=0) : from(a1),to(b1),cap(c1),flow(d1),cost(e1){} }; /**********************************************************/ int n,m,s,t; int mcost,cnt; Edge theEdge[MM_MAX*4]; vector<int> G[NN_MAX]; int d[NN_MAX],p[NN_MAX],a[NN_MAX]; bool inqee[NN_MAX]; /**********************************************************/ int min_2 (int x,int y) {return x<y?x:y;} int max_2 (int x,int y) {return x>y?x:y;} bool mcmf (); void Init (); /**********************************************************/ int main() { if (READ_FILE) freopen ("in.txt","r",stdin); while(scanf ("%d %d",&n,&m)!=EOF) { Init (); int a,b,c; cnt=0; for (int i = 0; i < m; i++){ scanf ("%d%d%d",&a,&b,&c);//无向边,两个正反,共四个边 theEdge[cnt++]=Edge (a,b,1,0,c);G[a].push_back (cnt-1); theEdge[cnt++]=Edge (b,a,0,0,-c);G[b].push_back (cnt-1); theEdge[cnt++]=Edge (b,a,1,0,c);G[b].push_back (cnt-1); theEdge[cnt++]=Edge (a,b,0,0,-c);G[a].push_back (cnt-1); } theEdge[cnt++]=Edge (0,1,2,0,0); G[0].push_back (cnt-1);//0 n+1是超级起点和超级终点 theEdge[cnt++]=Edge (1,0,0,0,0); G[1].push_back (cnt-1); theEdge[cnt++]=Edge (n,n+1,2,0,0); G .push_back (cnt-1); theEdge[cnt++]=Edge (n+1,n,0,0,0); G[n+1].push_back (cnt-1); s=0,t=n+1; mcost=0; while( mcmf() ) ; printf ("%d\n",mcost); } return 0; } void Init () { for (int i=0;i<=n+1;i++) G[i].clear (); } bool mcmf () { for (int i=0;i<=n+1;i++) d[i]=INF; memset (inqee,false,sizeof (inqee)); d[s]=0; inqee[s]=1;p[s]=s;a[s]=INF; queue<int> qee; qee.push (s); while (!qee.empty ()) { int x=qee.front ();qee.pop (); inqee[x]=0;//坑爹啊,就是这个错误卡了我两天,一个点可能进队多次 for (int i=0;i<G[x].size ();i++){ Edge e=theEdge[G[x][i]]; if (e.cap>e.flow && d[e.to]>d[x]+e.cost){//下一个点符合条件 d[e.to]=d[x]+e.cost;//最小费用 p[e.to]=G[x][i]; a[e.to]=min_2 (a[x],e.cap-e.flow);//最大流 if (!inqee[e.to]){ qee.push (e.to); inqee[e.to]=1; } } } } if (d[t]==INF) return false; mcost+=d[t]*a[t]; for (int i=t;i!=s;i=theEdge[p[i]].from){ theEdge[p[i]].flow+=a[t]; theEdge[p[i]^1].flow-=a[t]; } return true; }
邻接表的实现也可以在通过在Edge结构体中加入next变量,保存的是和它邻接的theEdge的下标。head[]数组保存的是某点最后一次出现在theEdge的下标。
例如,对于输入数据
4 5 2 4 2 1 2 1 3 4 1 2 3 1 1 3 2则结构体theEdge内容是
i-from-to-cap-cost-next 0-2 4 1 2 -1 1-4 2 0 -2 -1 2-4 2 1 2 1 3-2 4 0 -2 0 4-1 2 1 1 -1 5-2 1 0 -1 3 6-2 1 1 1 5 7-1 2 0 -1 4 8-3 4 1 1 -1 9-4 3 0 -1 2 10-4 3 1 1 9 11-3 4 0 -1 8 12-2 3 1 1 6 13-3 2 0 -1 11 14-3 2 1 1 13 15-2 3 0 -1 12 16-1 3 1 2 7 17-3 1 0 -2 14 18-3 1 1 2 17 19-1 3 0 -2 16 20-0 1 2 0 -1 21-1 0 0 0 19 22-4 5 2 0 10 23-5 4 0 0 -1head数组中的内容是:
0 1 2 3 4 5 20 21 15 18 22 23在for (int i=head[x];i!=-1;i=theEdge[i].next)循环中,首先x=0,取出head[0]=20,在theEdge[20]中将1入队,再取出head[1]=21(0)→19(3)→16(3)→7(2)→4(2)→-1,可以看出依次将邻接的边都取到了。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<string> #include<map> #include<set> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<sstream> #define LL long long #define OJ_PRINT 0 #define READ_FILE 0 using namespace std; const int NN_MAX = 1100; const int MM_MAX = 10000+10; const int INF = 0x1fffffff; struct Edge{ int from,to,cap,cost,next;//next 相当于邻接表下标 Edge (int a1=0,int b1=0,int c1=0,int d1=0,int e1=0):from(a1),to(b1),cap(c1),cost(d1),next(e1) {} }; /**********************************************************/ int n,m,s,t; int mcost,cnt; Edge theEdge[MM_MAX*4]; int head[NN_MAX]; //d[]用于求最小费用路径长度,即最短路。a[]用于保存最大流,d[i]*a[i]即是最小费用最大流 //p[]保存的是theEdge[]下标 int d[NN_MAX],p[NN_MAX],a[NN_MAX]; bool inqee[NN_MAX]; /**********************************************************/ int min_2 (int x,int y) {return x<y?x:y;} int max_2 (int x,int y) {return x>y?x:y;} bool mcmf (); /**********************************************************/ int main() { if (READ_FILE) freopen ("in.txt","r",stdin); while(scanf ("%d %d",&n,&m)!=EOF) { int a,b,c; cnt=0; memset (head,-1,sizeof (head)); for (int i = 0; i < m; i++){ scanf ("%d%d%d",&a,&b,&c);//无向边,两个正反,共四个边 theEdge[cnt]=Edge (a,b,1,c,head[a]);head[a]=cnt++; theEdge[cnt]=Edge (b,a,0,-c,head[b]);head[b]=cnt++; theEdge[cnt]=Edge (b,a,1,c,head[b]);head[b]=cnt++; theEdge[cnt]=Edge (a,b,0,-c,head[a]);head[a]=cnt++; } theEdge[cnt]=Edge (0,1,2,0,head[0]);head[0]=cnt++; theEdge[cnt]=Edge (1,0,0,0,head[1]);head[1]=cnt++; theEdge[cnt]=Edge (n,n+1,2,0,head );head =cnt++; theEdge[cnt]=Edge (n+1,n,0,0,head[n+1]);head[n+1]=cnt++; s=0,t=n+1; mcost=0; while( mcmf() ) ; printf ("%d\n",mcost); } return 0; } bool mcmf () { for (int i=0;i<=n+1;i++) d[i]=INF; d[s]=0; memset (inqee,false,sizeof (inqee)); p[s]=-1;a[s]=INF;inqee[s]=1; queue<int> qee; qee.push(s); while (!qee.empty ()) { int x=qee.front ();qee.pop (); inqee[x]=0;//x可能已经在前面过程中,被当做e.to入队,需要置零 for (int i=head[x];i!=-1;i=theEdge[i].next){ Edge e=theEdge[i]; if (e.cap>0 && d[e.to]>d[x]+e.cost){ d[e.to]=d[x]+e.cost; a[e.to]=min_2 (a[x],e.cap); p[e.to]=i; if (!inqee[e.to]){//若该点已被访问,且存在更短的路,更小的费用,则需要更新a[],d[],p[],但是不能再将该点入队 qee.push (e.to); inqee[e.to]=1; } } } } if (d[t]==INF) return false; mcost+=d[t]*a[t];//a[t]=1或0 for (int i=p[t]; i!=-1; i=p[theEdge[i].from]){ theEdge[i].cap-=a[t]; theEdge[i^1].cap+=a[t]; } return true; }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- C++ Custom Control控件向父窗体发送对应的消息
- C++中拷贝构造函数的应用详解