洛谷P4553 80人环游世界

题意:有m个人,给定n个城市构成DAG。

每个城市恰好有vi个人经过,每条边有费用。

每个人自选起点,终点。

求最小费用。

解:

首先拆点,连边,流量为[vi, vi]。

然后做有上下界有源汇最小费用可行流即可。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <queue>
  4 #include <cstring>
  5 #include <cmath>
  6 
  7 const int N = 18000, M = 1000010, INF = 0x3f3f3f3f;
  8 
  9 struct Edge {
 10     int nex, v, c, len;
 11 }edge[M << 1]; int top = 1;
 12 
 13 int e[N], d[N], vis[N], pre[N], flow[N];
 14 std::queue<int> Q;
 15 bool vp[200010];
 16 
 17 inline void add(int x, int y, int z, int w) {
 18     top++;
 19     edge[top].v = y;
 20     edge[top].c = z;
 21     edge[top].len = w;
 22     edge[top].nex = e[x];
 23     e[x] = top;
 24 
 25     top++;
 26     edge[top].v = x;
 27     edge[top].c = 0;
 28     edge[top].len = -w;
 29     edge[top].nex = e[y];
 30     e[y] = top;
 31     return;
 32 }
 33 
 34 inline bool SPFA(int s, int t) {
 35     memset(d, 0x3f, sizeof(d));
 36     d[s] = 0;
 37     flow[s] = INF;
 38     vis[s] = 1;
 39     Q.push(s);
 40     while(!Q.empty()) {
 41         int x = Q.front();
 42         Q.pop();
 43         vis[x] = 0;
 44         for(int i = e[x]; i; i = edge[i].nex) {
 45             int y = edge[i].v;
 46             if(edge[i].c && d[y] > d[x] + edge[i].len) {
 47                 d[y] = d[x] + edge[i].len;
 48                 pre[y] = i;
 49                 flow[y] = std::min(flow[x], edge[i].c);
 50                 if(!vis[y]) {
 51                     vis[y] = 1;
 52                     Q.push(y);
 53                 }
 54             }
 55         }
 56     }
 57     return d[t] < INF;
 58 }
 59 
 60 inline void update(int s, int t) {
 61     int temp = flow[t];
 62     while(t != s) {
 63         int i = pre[t];
 64         edge[i].c -= temp;
 65         edge[i ^ 1].c += temp;
 66         t = edge[i ^ 1].v;
 67     }
 68     return;
 69 }
 70 
 71 inline int solve(int s, int t, int &cost) {
 72     int ans = 0;
 73     cost = 0;
 74     while(SPFA(s, t)) {
 75         ans += flow[t];
 76         cost += flow[t] * d[t];
 77         update(s, t);
 78     }
 79     return ans;
 80 }
 81 
 82 
 83 int main() {
 84 
 85     int n, m;
 86     scanf("%d%d", &n, &m);
 87     int s = 2 * n + 1, t = 2 * n + 2, ss = 2 * n + 3, S = n * 2 + 4, T = n * 2 + 5;
 88     for(int i = 1, x; i <= n; i++) {
 89         scanf("%d", &x);
 90         // add  i  i + n  x
 91         add(S, i + n, x, 0);
 92         add(i, T, x, 0);
 93     }
 94     for(int i = 1, x; i <= n; i++) {
 95         for(int j = i + 1; j <= n; j++) {
 96             scanf("%d", &x);
 97             if(x == -1) {
 98                 continue;
 99             }
100             add(i + n, j, INF, x);
101         }
102         add(ss, i, INF, 0);
103         add(i + n, t, INF, 0);
104     }
105     add(s, ss, m, 0);
106     add(t, s, INF, 0);
107     int ans;
108     solve(S, T, ans);
109     printf("%d", ans);
110     return 0;
111 }
AC代码

 

转载于:https://www.cnblogs.com/huyufeifei/p/10103414.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值