[AHOI2014/JSOI2014]支线剧情 有上下界费用流
题解:
第一眼费用流,,然后想了好久怎么建图,,,最后发现是最小费用可行流的板子题。。。。
其实还没有很懂这个算法,所以这里只是摆一下步骤,以后再补理解吧。
首先一个思路就是转换图,将有上下限的图变为普通的网络流图,然后再跑费用流。
所以建图其实和有上下界的网络流一样的。。。
1,首先建立超级源点汇点ss和tt
2,对于原图中每一条边x ---> y,设其上下界为(l, r),费用为cost,那么连边的时候将流量变为r - l即可
3,对于任意点i,记d[i]为它的富余流量,即入度的下界和 - 出度的下界和。
若d[i] > 0,则连边ss ----> i, 流量为d[i] , 费用0
若d[i] < 0,则连边i ----> tt,流量为-d[i],费用0
4,连边t ---> s,流量inf,费用0
答案即为ans + 所有边下界 * 边的费用
其实可以感性的理解为先有了一个不一定合法的解(下界*费用),然后再利用费用流使用最小的代价使得答案合法。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 400 5 #define ac 100000 6 #define inf 2139062143 7 #define getchar() *o++ 8 char READ[5001000], *o = READ; 9 int n, ans, s, t1, t; 10 int last[AC], dis[AC], disflow[AC], d[AC]; 11 int date[ac], Next[ac], haveflow[ac], cost[ac], Head[AC], tot = 1; 12 bool z[AC]; 13 deque<int> q; 14 15 inline int read() 16 { 17 int x=0;char c=getchar(); 18 while(c > '9' || c < '0') c = getchar(); 19 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 20 return x; 21 } 22 23 inline void add(int f,int w,int S,int C) 24 { 25 date[++tot] = w, Next[tot] = Head[f], haveflow[tot] = S, cost[tot] = C, Head[f] = tot; 26 date[++tot] = f, Next[tot] = Head[w], cost[tot] = -C, Head[w] = tot; 27 //printf("%d %d %d %d\n",f,w,S,C); 28 } 29 30 void pre() 31 { 32 int a,b,c; 33 n=read(); 34 t1 = n + 1, s = n + 2, t = n + 3; 35 for(R i=1;i<=n;i++) 36 { 37 a=read(); 38 for(R j=1;j<=a;j++) 39 { 40 b=read(),c=read(); 41 --d[i], ++d[b]; 42 ans += c; 43 add(i, b, inf, c); 44 } 45 } 46 for(R i=2;i<=n;i++) 47 add(i, t1, inf, 0); 48 for(R i=1;i<=n;i++) 49 { 50 if(d[i] > 0) add(s, i, d[i], 0); 51 if(d[i] < 0) add(i, t, -d[i], 0); 52 } 53 add(t1, 1, inf, 0);//是费用为0! 54 } 55 56 inline void aru() 57 { 58 int x = t; 59 while(x != s) 60 { 61 haveflow[last[x]] -= disflow[t]; 62 haveflow[last[x] ^ 1] += disflow[t]; 63 x = date[last[x] ^ 1]; 64 } 65 ans += disflow[t] * dis[t]; 66 } 67 68 bool spfa() 69 { 70 int x, now; 71 dis[s] = 0, disflow[s] = inf, z[s] = true; 72 q.push_front(s); 73 while(!q.empty()) 74 { 75 x = q.front(); 76 q.pop_front(); 77 z[x] = false;//标记出列 78 for(R i=Head[x]; i ;i=Next[i]) 79 { 80 now = date[i]; 81 if(haveflow[i] && dis[now] > dis[x] + cost[i]) 82 {//要有流量啊 83 dis[now] = dis[x] + cost[i]; 84 last[now] = i; 85 disflow[now] = min(disflow[x], haveflow[i]); 86 if(!z[now]) 87 { 88 z[now] = true; 89 if(!q.empty() && dis[now] < q.front()) q.push_front(now); 90 else q.push_back(now); 91 } 92 } 93 } 94 } 95 if(dis[t] != inf) aru(); 96 return dis[t] != inf; 97 } 98 99 bool spfa1() 100 { 101 int x,now; 102 z[s]=true,dis[s]=0,disflow[s]=inf; 103 q.push_front(s); 104 while(!q.empty()) 105 { 106 x=q.front(); 107 q.pop_front(); 108 z[x]=false; 109 for(int i=Head[x]; i ;i=Next[i]) 110 { 111 now=date[i]; 112 if(haveflow[i] && dis[now]>dis[x]+cost[i]) 113 { 114 dis[now]=dis[x]+cost[i]; 115 last[now]=i; 116 disflow[now]=min(disflow[x],haveflow[i]);//以点为单位记录到这个点时的流量 117 if(!z[now]) 118 { 119 z[now] = true; 120 q.push_front(now); 121 } 122 /*if(!z[now] && now!=t) 123 { 124 if(!q.empty() && dis[now]<dis[q.front()]) q.push_front(now); 125 else q.push_back(now); 126 z[now]=true; 127 }*/ 128 } 129 } 130 } 131 //更新路径 132 if(dis[t] != inf) aru(); 133 return dis[t] != inf; 134 } 135 136 void work() 137 { 138 //printf("%d\n",ans); 139 memset(dis, 127, sizeof(dis)); 140 while(spfa()) 141 memset(dis, 127, sizeof(dis)); 142 printf("%d\n",ans); 143 } 144 145 int main() 146 { 147 // freopen("in.in","r",stdin); 148 fread(READ, 1, 5000000, stdin); 149 pre(); 150 work(); 151 // fclose(stdin); 152 return 0; 153 }