UVALive 7264 Kejin Game 网络流+最小割

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 }
View Code

第一次网络流拆点, 不是很明白为什么这样建边就好了, 想着各种将点与点各种关系连起来, 但是却没有办法实现求解。 需要加强建边的思维。

猜你喜欢

转载自www.cnblogs.com/MingSD/p/9340933.html