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 }

转载于:https://www.cnblogs.com/lytccc/p/7010161.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值