UVALive 7264 Kejin Game 网络流+最小割
题意:一个人有一颗技能树, 现在它想修练到某个技能 (假设为x), 现在修一个技能有3种方式: 1, 将该技能的前置技能都学完了,才能学该技能。 2, 取消一个技能 与 另一个技能的前置关系, 也就是说修该技能的时候不需要先修取消了关系的前置技能。 3,无视前置关系, 直接修某个技能。 这3种方式都是需要花费一定的代价的,求修的S之后的最小代价。
题解:网络流拆点, 把所有的点都复制一份, 每一个 i 都会对应一个 i' , 然后0为源点, 将(s,i) 相连, 流量上限为(修完前置技能后) 修该技能的花费。 将(i, i') 相连, 流量上限为直接修得该技能的花费。
如果u 是 v的前置技能, 那么就将(u',v)建边, 花费为取消该技能的花费。 最后将 (x', t) 相连, 流量上限为 inf。 这样建完图之后, 我们就可以发现, 最小割就是修的 x 的最小花费。
图片转载 Form here
图上的1, 2, 3, 4 这4种割, 每种割法都可以修得目标技能, 那么最小割就是最后的解了。 最后 建完边之后跑出最大流 , 就是解了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 4 #define LL long long 5 #define ULL unsigned LL 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define lson l,m,rt<<1 10 #define rson m+1,r,rt<<1|1 11 #define max3(a,b,c) max(a,max(b,c)) 12 #define min3(a,b,c) min(a,min(b,c)) 13 typedef pair<int,int> pll; 14 const int inf = 0x3f3f3f3f; 15 const LL INF = 0x3f3f3f3f3f3f3f3f; 16 const LL mod = (int)1e9+7; 17 const int N =1010; 18 const int M = N*100; 19 int n, m, s, t; 20 int head[N], to[M], nx[M], w[M]; 21 int deep[N], cur[N]; 22 int tot; 23 void add(int u, int v, int val){ 24 w[tot] = val; 25 to[tot] = v; 26 nx[tot] = head[u]; 27 head[u] = tot++; 28 } 29 int bfs(int s, int t){ 30 queue<int> q; 31 memset(deep, 0, sizeof deep); 32 q.push(s); 33 deep[s] = 1; 34 while(!q.empty()){ 35 int u = q.front(); 36 q.pop(); 37 for(int i = head[u]; ~i; i = nx[i]){ 38 if(w[i] > 0 && deep[to[i]] == 0){ 39 deep[to[i]] = deep[u] + 1; 40 q.push(to[i]); 41 } 42 } 43 } 44 if(deep[t] > 0) return 1; 45 return 0; 46 } 47 int Dfs(int u, int t, int flow){ 48 if(u == t) return flow; 49 for(int &i = cur[u]; ~i; i = nx[i]){ 50 if(deep[u]+1 == deep[to[i]] && w[i] > 0){ 51 int di = Dfs(to[i], t, min(w[i], flow)); 52 if(di > 0){ 53 w[i] -= di, w[i^1] += di; 54 return di; 55 } 56 } 57 } 58 return 0; 59 } 60 int Dinic(int s, int t){ 61 int ans = 0, tmp; 62 while(bfs(s, t)){ 63 for(int i = 0; i <= n*2+1; i++) cur[i] = head[i]; 64 while(tmp = Dfs(s, t, inf)) ans += tmp; 65 } 66 return ans; 67 } 68 void init(){ 69 memset(head, -1, sizeof(head)); 70 tot = 0; 71 } 72 int main(){ 73 scanf("%d", &t); 74 while(t--){ 75 scanf("%d%d%d", &n, &m, &s); 76 init(); 77 int a, b, c; 78 for(int i = 1; i <= m; i++){ 79 scanf("%d%d%d", &a, &b, &c); 80 add(a+n,b,c); 81 add(b,a+n,0); 82 } 83 for(int i = 1; i <= n; i++){ 84 scanf("%d", &a); 85 add(0,i,a); 86 add(i,0,0); 87 } 88 for(int i = 1; i <= n; i++){ 89 scanf("%d", &a); 90 add(i, i+n, a); 91 add (i+n, i, 0); 92 } 93 add(s+n,2*n+1,inf); 94 add(2*n+1,s+n,0); 95 printf("%d\n", Dinic(0,2*n+1)); 96 } 97 return 0; 98 }
第一次网络流拆点, 不是很明白为什么这样建边就好了, 想着各种将点与点各种关系连起来, 但是却没有办法实现求解。 需要加强建边的思维。