USACO 4.4 Pollutant Control(最小割,求最优解)

2015-03-25 16:52:22

思路:题意说了半天其实就是求最小割,然后输出。并且要求(1)流量相等时边数较小,(2)流量边数相等时要求边的编号字典序最小。

  关于最小割,浏览了相关资料和07胡伯涛论文... 大概知道:

   (1)最大流流量= 最小割流量。

   (2)最小割把点分成S,T,其中起点 s ∈ S,终点 t ∈ T,割边就是e = {(u,v) | u ∈ S , v ∈ T}。

   (3)最小割的边都满流,但满流的边并不一定属于最小割。 

  对于这题:

  首先,要处理要求(1),看了别人的博客... 看到个很神的方法,把每条边的流量 × (M+1)+1,再跑最大流,得到Max_flow。

  那么最大流流量为 Max_flow / (M+1),最小割的边数为 Max_flow % (M+1)。(思考!)

  原因:不作处理的最大流仅能求出最小割的流量,但不能使边数较小。所以我们尝试给每条边附加一个权值又不至于影响最小割结果。

  其次,要处理要求(2)及其输出解,可以从起点做一次 floodfill(其实就是从起点出发的DFS,沿着未满流的边的走),标记 S 点集。

  再从终点做一次 floodfill(这是一种反向遍历),标记 T 点集合。那么 e = {(u,v) | (u∈S&&v∈T) || (u∉S&&v∉T)}。

  也就是说某边为割边当且仅当该边的两个点不同时在S中或T中。

  要保证边的编号的字典序最小只要按照输入的顺序检索所有边即可,直到符合要求边的数量 == 最小割边数。

  1 /*
  2 ID:naturec1
  3 PROG: milk6
  4 LANG: C++
  5 */
  6 #include <cstdio>
  7 #include <cstring>
  8 #include <cstdlib>
  9 #include <cmath>
 10 #include <vector>
 11 #include <map>
 12 #include <set>
 13 #include <stack>
 14 #include <queue>
 15 #include <string>
 16 #include <iostream>
 17 #include <algorithm>
 18 using namespace std;
 19 
 20 #define MEM(a,b) memset(a,b,sizeof(a))
 21 #define REP(i,n) for(int i=1;i<=(n);++i)
 22 #define REV(i,n) for(int i=(n);i>=1;--i)
 23 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
 24 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i)
 25 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
 26 #define MP(a,b) make_pair(a,b)
 27 
 28 typedef long long ll;
 29 typedef pair<int,int> pii;
 30 const int INF = (1 << 30) - 1;
 31 const int MAXN = 50;
 32 const int MAXM = 1010;
 33 
 34 int N,M,S[MAXN];
 35 struct edge{
 36     int u,v,next,id;
 37     ll cp;
 38 };
 39 
 40 struct Max_flow{
 41     int first[MAXN],ecnt;
 42     int st,ed,lev[MAXN];
 43     edge e[MAXM * 8];
 44     void init(int tst,int ted){
 45         MEM(first,-1);
 46         ecnt = 0;
 47         st = tst;
 48         ed = ted;
 49     }
 50     void add_edge(int u,int v,ll c,int id){
 51         e[ecnt].next = first[u];
 52         e[ecnt].u = u;
 53         e[ecnt].v = v;
 54         e[ecnt].id = id;
 55         e[ecnt].cp = c;
 56         first[u] = ecnt++;
 57 
 58         e[ecnt].next = first[v];
 59         e[ecnt].u = v;
 60         e[ecnt].v = u;
 61         e[ecnt].id = id;
 62         e[ecnt].cp = 0;
 63         first[v] = ecnt++;
 64     }
 65     bool bfs(){
 66         queue<int> Q;
 67         Q.push(st);
 68         MEM(lev,-1);
 69         lev[st] = 0;
 70         while(!Q.empty()){
 71             int cur = Q.front(); Q.pop();
 72             for(int i = first[cur]; ~i; i = e[i].next){
 73                 int v = e[i].v;
 74                 if(lev[v] < 0 && e[i].cp > 0){
 75                     lev[v] = lev[cur] + 1;
 76                     Q.push(v);
 77                 }
 78             }
 79         }
 80         return lev[ed] != -1;
 81     }
 82     ll dfs(int p,ll minf){
 83         if(p == ed) return minf;
 84         for(int i = first[p]; ~i; i = e[i].next){
 85             int v = e[i].v;
 86             if(lev[v] > lev[p] && e[i].cp > 0){
 87                 ll d = dfs(v,min(e[i].cp,minf));
 88                 if(d > 0){
 89                     e[i].cp -= d;
 90                     e[i ^ 1].cp += d;
 91                     return d;
 92                 }
 93             }
 94         }
 95         return 0;
 96     }
 97     ll dinic(){
 98         ll max_flow = 0,cur;
 99         while(bfs()){
100             while((cur = dfs(st,1LL << 60)) > 0)
101                 max_flow += cur;
102         }
103         return max_flow;
104     }
105     void Flood_fill(int p){
106         S[p] = 1;
107         for(int i = first[p]; ~i; i = e[i].next){
108             int v = e[i].v;
109             if(S[v] || e[i].cp <= 0) continue;
110             Flood_fill(v);
111         }
112     }
113     void A_Flood_fill(int p){
114         S[p] = -1;
115         for(int i = first[p]; ~i; i = e[i].next){
116             int v = e[i].v;
117             if(S[v] || e[i ^ 1].cp <= 0) continue;
118             A_Flood_fill(v);
119         }
120     }
121 }MF;
122 
123 int main(){
124     freopen("milk6.in","r",stdin);
125     freopen("milk6.out","w",stdout);
126     int a,b;
127     ll c;
128     scanf("%d%d",&N,&M);
129     MF.init(1,N);
130     REP(i,M){
131         scanf("%d%d%lld",&a,&b,&c);
132         c = c * (M + 1) + 1;
133         MF.add_edge(a,b,c,i);
134         //MF.add_edge(b,a,c,i);
135     }
136     ll tmax_flow = MF.dinic();
137     MF.Flood_fill(MF.st);
138     MF.A_Flood_fill(MF.ed);
139     ll anscnt = tmax_flow % (M + 1);
140     printf("%lld %lld\n",tmax_flow / (M + 1),anscnt);
141     if(tmax_flow){
142         int tcnt = 0;
143         for(int i = 0; tcnt < anscnt && i < MF.ecnt; i += 2){
144             int tmp = S[MF.e[i].u] + S[MF.e[i].v];
145             if(MF.e[i].cp == 0 && tmp != 2 && tmp != -2){
146                 printf("%d\n",MF.e[i].id);
147                 ++tcnt;
148             }
149         }
150     }
151     return 0;
152 }

 

转载于:https://www.cnblogs.com/naturepengchen/articles/4366277.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值