HDU 3987 Harry Potter and the Forbidden Forest

题意:给出一张有n个点的图,有的边又向,有的边无向,现在要你破坏一些路,使得从点0无法到达点n-1。破坏每条路都有一个代价。求在代价最小的前提下,最少需要破坏多少条道路。(就是说求在最小割的前提下,最小的割边数)

这题做了长姿势了。偏移量的方法没有想到。如果采用偏移量的方法的话,把每条边的边权w设为w'=w*MOD+1,然后求最小割,那么maxflow%MOD就是答案了。前提是MOD足够大。

另外一种方法,利用一个结论:最小割边一定满流,满流的未必是最小割边。假设所有满流的边都是最小割边,把它们的cap全部设为1,其余未满流的边的cap设为inf,然后求最小割,答案就是最小割值。虽然没搞懂怎么证明这个做法是对的,也觉得这个做法有点邪乎,但它就是对的。也就是说,最小割不唯一的情况下,未出现在一个最小割中却出现在另外一个最小割中的边一定也是满流的(真不知道到底对不对)。

代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define INF 1<<30
 6 #define maxn 1010
 7 #define maxm 500000
 8 using namespace std;
 9 
10 int v[maxm],next[maxm],w[maxm];
11 int first[maxn],d[maxn],work[maxn],q[maxn];
12 int e,S,T;
13 
14 void init(){
15     e = 0;
16     memset(first,-1,sizeof(first));
17 }
18 
19 void add_edge(int a,int b,int c){
20     v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++;
21     v[e] = a;next[e] = first[b];w[e] = 0;first[b] = e++;
22 }
23 
24 int bfs(){
25     int rear = 0;
26     memset(d,-1,sizeof(d));
27     d[S] = 0;q[rear++] = S;
28     for(int i = 0;i < rear;i++){
29         for(int j = first[q[i]];j != -1;j = next[j])
30             if(w[j] && d[v[j]] == -1){
31                 d[v[j]] = d[q[i]] + 1;
32                 q[rear++] = v[j];
33                 if(v[j] == T)   return 1;
34             }
35     }
36     return 0;
37 }
38 
39 int dfs(int cur,int a){
40     if(cur == T)    return a;
41     for(int &i = work[cur];i != -1;i = next[i]){
42         if(w[i] && d[v[i]] == d[cur] + 1)
43             if(int t = dfs(v[i],min(a,w[i]))){
44                 w[i] -= t;w[i^1] += t;
45                 return t;
46             }
47     }
48     return 0;
49 }
50 
51 int dinic(){
52     int ans = 0;
53     while(bfs()){
54         memcpy(work,first,sizeof(first));
55         while(int t = dfs(S,INF))   ans += t;
56     }
57     return ans;
58 }
59 
60 int main()
61 {
62     int n,m,nkase;
63     scanf("%d",&nkase);
64     for(int kase = 1;kase <= nkase;kase++){
65         init();
66         scanf("%d%d",&n,&m);
67         S = 0,T = n-1;
68         for(int i = 0;i < m;i++){
69             int a,b,c,d;
70             scanf("%d%d%d%d",&a,&b,&c,&d);
71             add_edge(a,b,c);
72             if(d == 1)  add_edge(b,a,c);
73         }
74         dinic();
75         for(int i = 0;i < e;i += 2){
76             if(w[i] == 0){
77                 w[i] = 1;
78                 w[i^1] = 0;
79             }else{
80                 w[i] = INF;
81                 w[i^1] = 0;
82             }
83         }
84         printf("Case %d: %d
",kase,dinic());
85     }
86     return 0;
87 }
View Code
原文地址:https://www.cnblogs.com/zhexipinnong/p/3400230.html