BZOJ 2055 80人环游世界
80人环游世界
【问题描述】
想必大家都看过成龙大哥的《80天环游世界》,里面的紧张刺激的打斗场面一定给你留下了深刻的印象。现在就有这么一个80人的团伙,也想来一次环游世界。
他们打算兵分多路,游遍每一个国家。
因为他们主要分布在东方,所以他们只朝西方进军。设从东方到西方的每一个国家的编号依次为1...N。假若第i个人的游历路线为P1、P2......Pk(0≤k≤N),则P1<P2<......<Pk。
众所周知,中国相当美丽,这样在环游世界时就有很多人经过中国。我们用一个正整数Vi来描述一个国家的吸引程度,Vi值越大表示该国家越有吸引力,同时也表示有且仅有Vi个人会经过那一个国家。
为了节省时间,他们打算通过坐飞机来完成环游世界的任务。同时为了省钱,他们希望总的机票费最小。
明天就要出发了,可是有些人临阵脱逃,最终只剩下了M个人去环游世界。他们想知道最少的总费用,你能告诉他们吗?
【输入格式】
第一行两个正整数N,M。
第二行有N个不大于M正整数,分别表示V1,V2......VN。
接下来有N-1行。第i行有N-i个整数,该行的第j个数表示从第i个国家到第i+j个国家的机票费(如果该值等于-1则表示这两个国家间没有通航)。
【输出格式】
在第一行输出最少的总费用。
【样例输入】
6 3
2 1 3 1 2 1
2 6 8 5 0
8 2 4 1
6 1 0
4 -1
4
【样例输出】
27
【样例输出】
1<= N < =100 1<= M <= 79
题解:
题意是求 m 个人(每个人起始点任意)通过路径经过第 i 个国家恰好 v[i] 次的最小费用
考虑有上下界无源汇可行最小费用循环流
每个点拆成两个点,一个入,一个出
一个点向另一个点连上界下界均为 v[i] 的边,其它边下界均为 0
对于流量为 m 和任意起始点要求
我们新建一个点,从超级源向这个点连容量为 m 的边
再从这个点向每一个入点连一条无上界的边
其实就是在原图中的源点向这个点连下界为 m 的边
其余的按照题意建边
1 #include<cmath> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 const int maxn = 233; 9 const int maxm = maxn * maxn; 10 const int inf = 1e9; 11 int num; 12 int fir[maxn], nex[maxm], ver[maxm], con[maxm], pri[maxm]; 13 inline void Scan(int &x) 14 { 15 char c; 16 bool o = false; 17 while(!isdigit(c = getchar())) o = (c != '-') ? o : true; 18 x = c - '0'; 19 while(isdigit(c = getchar())) x = x * 10 + c - '0'; 20 if(o) x = -x; 21 } 22 inline void Ins(int x, int y, int z, int v) 23 { 24 nex[++num] = fir[x]; 25 fir[x] = num; 26 ver[num] = y; 27 con[num] = z; 28 pri[num] = v; 29 } 30 inline void Add(int x, int y, int z, int v) 31 { 32 printf("%d %d %d\n", x, y, z); 33 Ins(x, y, z, v); 34 Ins(y, x, 0, -v); 35 } 36 int du[maxn]; 37 inline void Put(int x, int y, int l, int r, int v) 38 { 39 du[x] -= l, du[y] += l; 40 Add(x, y, r - l, v); 41 } 42 int s, t, nors, nort, supers, supert; 43 int que[maxm], dis[maxn]; 44 bool vis[maxn]; 45 inline bool Spfa() 46 { 47 int head = 0, tail = 1; 48 memset(dis, 127 / 2, sizeof(dis)); 49 que[tail] = s; 50 dis[s] = 0; 51 vis[s] = true; 52 while(head < tail) 53 { 54 int u = que[++head]; 55 for(int i = fir[u]; i; i = nex[i]) 56 { 57 if(!con[i]) continue; 58 int v = ver[i]; 59 if(dis[v] > dis[u] + pri[i]) 60 { 61 dis[v] = dis[u] + pri[i]; 62 if(!vis[v]) 63 { 64 vis[v] = true; 65 que[++tail] = v; 66 } 67 } 68 } 69 vis[u] = false; 70 } 71 return dis[t] < inf; 72 } 73 int ans; 74 bool mark[maxn]; 75 int Dinic(int u, int flow) 76 { 77 mark[u] = true; 78 if(u == t) return flow; 79 int left = flow; 80 for(int i = fir[u]; i; i = nex[i]) 81 { 82 if(!con[i]) continue; 83 int v = ver[i]; 84 if(mark[v] || dis[v] != dis[u] + pri[i]) continue; 85 int go = Dinic(v, min(con[i], left)); 86 if(go) 87 { 88 con[i] -= go; 89 con[i ^ 1] += go; 90 left -= go; 91 ans += go * pri[i]; 92 if(!left) return flow; 93 } 94 } 95 return flow - left; 96 } 97 int n, m; 98 inline void Set() 99 { 100 num = 1; 101 nors = n << 1 | 1; 102 nort = nors + 1; 103 supers = nort + 1; 104 supert = supers + 1; 105 } 106 inline void Flow(int x, int y) 107 { 108 s = x, t = y; 109 while(Spfa()) 110 { 111 memset(mark, false, sizeof(mark)); 112 Dinic(s, inf); 113 } 114 } 115 int main() 116 { 117 Scan(n), Scan(m); 118 Set(); 119 int v; 120 for(int i = 1; i <= n; ++i) 121 { 122 Scan(v); 123 Add(nors, i, inf, 0); 124 Put(i, i + n, v, v, 0); 125 } 126 for(int i = 1; i <= n; ++i) 127 for(int j = i + 1; j <= n; ++j) 128 { 129 Scan(v); 130 if(v != -1) Add(i + n, j, inf, v); 131 } 132 for(int i = 1; i <= supert; ++i) 133 { 134 if(du[i] > 0) Add(supers, i, du[i], 0); 135 if(du[i] < 0) Add(i, supert, -du[i], 0); 136 } 137 Add(supers, nors, m, 0); 138 Flow(supers, supert); 139 printf("%d", ans); 140 }