坐井观天

In the name of dream

导航

POJ 2135 最小费用流入门题

Posted on 2012-06-19 17:29  一毛_  阅读(551)  评论(1编辑  收藏  举报

题目链接: http://poj.org/problem?id=2135

题目大意:

  给定一个图,问从1到n,再从n回到1,要求边不重复地走,最短的路是多少。

 

分析:

  (看题解的)每条边只能用一次,所以容量是1, 费用即原图的边权,添加源点ST=0, ST向1连一条边,容量为2(1到n,再n到1,相当于从1到n两次),费用为0,(汇点可以不添加,把n直接看作汇点是可以的)

  每次在残余网络中找最小费用增广路(spfa找最短路)并增广、修改残余网络,直到不存在残余网络为止。

 

这题一直错,第一次写费用流,注意:

  1、 有负边权,采用spfa,注意加cnt[]数组判断负环;

  2、 采用了循环队列,所以h++, if( h==maxn ) h= 0;

  3、 这题是无向图,所以需要加双向边(容量相等,边权相等),但是每条边在残余网络里又存在一个与之对应的反向边(容量对称,边权对称,即正向边的边权是w,反边的则是-w),所以总的边数是4*M, 开始我就一直开小了, 我和lin神对于反边边权为-w 的理解是,反向边在最大流里是表示正向已经流过,之后发现了有其他路可以调整路径,所以回流的话,相应的边权应该抵消。

  4、 本题的spfa的我开始由于写Edmond_karp的习惯写了 if( v==ED ) return 1; 这是错误的,spfa必须在循环结束之后才更新出所有的最短路,这点自己居然忘记了。 具体见代码了。

 

代码:

poj2135
 1 /*2135    Accepted    1060K    16MS    C++    2710B    2012-06-19 17:19:21*/
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <iostream>
 6 #include <algorithm>
 7 #include <vector>
 8 using namespace std;
 9 
10 #define mpair make_pair
11 #define pii pair<int,int>
12 #define MM(a,b) memset(a,b,sizeof(a));
13 typedef long long lld;
14 typedef unsigned long long u64;
15 template<class T> bool up_max(T& a,const T& b){return b>a? a=b,1 : 0;}
16 template<class T> bool up_min(T& a,const T& b){return b<a? a=b,1 : 0;}
17 #define maxn 1010
18 const int inf= 2100000000;
19 
20 int n,m;
21 int ST, ED;
22 int top, head[maxn];
23 struct Edge{
24     int u, v, c, f, next; ///c: cost,  f: flow;
25     Edge(){}
26     Edge(int u, int v, int c, int f, int next): u(u),v(v),c(c),f(f),next(next){}
27 } edge[40080];
28 void Addedge(int u,int v,int c,int f){
29     edge[top]= Edge( u, v, c, f, head[u] ); head[u]= top++;
30     edge[top]= Edge( v, u, -c, 0, head[v] ); head[v]= top++;
31 }
32 
33 bool vis[maxn];
34 int pre[maxn]; /// record the pre-edge's index, for modifying the remain-network;
35 int que[maxn+10];
36 int dis[maxn], cnt[maxn];
37 bool spfa(){
38     for(int i=ST;i<=ED;++i) dis[i]= inf, cnt[i]=vis[i]= 0, pre[i]= -1;
39     int h= 0, tail= 0;
40     que[tail++]= ST;
41     dis[ST]= 0;
42     vis[ST]= 1, cnt[ST]= 1;
43     while( h!=tail ){  /// h!=tail; because it it circle-queue;
44         int u= que[h++];
45         if( h==maxn ) h= 0;
46         vis[u]= 0;
47         for(int i=head[u];i!=-1;i=edge[i].next){
48             int v= edge[i].v;
49             if( edge[i].f>0 && up_min( dis[v], dis[u]+edge[i].c ) ){
50                 pre[v]= i; ///
51                 if( !vis[v] ){
52                     vis[v]= 1;
53                     ++cnt[v];
54                     que[tail++]= v;
55                     if( tail==maxn ) tail= 0;
56                     if( cnt[v]>ED ) return 0;
57                 }
58             }
59         }
60     }
61     if( pre[ED] == -1 ) return 0;
62     return 1;
63 }
64 
65 int solve(){
66     int ret= 0, mf=0;
67     while( spfa() ){
68         int t= inf;
69         for(int i=pre[ED];i!=-1;i= pre[ edge[i].u ] )
70             up_min( t, edge[i].f );  /// t == 1 (must);
71         for(int i=pre[ED];i!=-1;i= pre[ edge[i].u ] ){
72             edge[ i ].f -= t;
73             edge[ i^1 ].f += t; /// i^1;
74         }
75         ret+= dis[ED];
76     }
77     return ret;
78 }
79 
80 void build_graph(){
81     top= 0;
82     fill( head, head+1+n, -1 );
83     while(m--){
84         int u, v, c;
85         scanf("%d%d%d", &u, &v, &c);
86         Addedge( u, v, c, 1 );
87         Addedge( v, u, c, 1 );
88     }
89     ST= 0, ED= n;
90     Addedge( ST, 1, 0, 2 );
91 }
92 
93 int main(){
94     //freopen("poj2135.in","r",stdin);
95     while( cin>>n>>m ){
96         build_graph();
97         cout<< solve() <<endl;
98     }
99 }