[BZOJ2055]80人环游世界 有上下界最小费用最大流

2055: 80人环游世界

Time Limit: 10 Sec  Memory Limit: 64 MB

Description

    想必大家都看过成龙大哥的《80天环游世界》,里面的紧张刺激的打斗场面一定给你留下了深刻的印象。现在就有这么
    一个80人的团伙,也想来一次环游世界。
    他们打算兵分多路,游遍每一个国家。
    因为他们主要分布在东方,所以他们只朝西方进军。设从东方到西方的每一个国家的编号依次为1...N。假若第i个人的游历路线为P1、P2......Pk(0≤k≤N),则P1<P2<......<Pk。
    众所周知,中国相当美丽,这样在环游世界时就有很多人经过中国。我们用一个正整数Vi来描述一个国家的吸引程度,Vi值越大表示该国家越有吸引力,同时也表示有且仅
有Vi个人会经过那一个国家。
    为了节省时间,他们打算通过坐飞机来完成环游世界的任务。同时为了省钱,他们希望总的机票费最小。
    明天就要出发了,可是有些人临阵脱逃,最终只剩下了M个人去环游世界。他们想知道最少的总费用,你能告诉他们吗?

Input

第一行两个正整数N,M。
第二行有N个不大于M正整数,分别表示V1,V2......VN。
接下来有N-1行。第i行有N-i个整数,该行的第j个数表示从第i个国家到第i+j个国家的机票费(如果该值等于-1则表示这两个国家间没有通航)。

Output

在第一行输出最少的总费用。

Sample Input

6 3
2 1 3 1 2 1
2 6 8 5 0
8 2 4 1
6 1 0
4 -1
4

Sample Output

27

HINT

1<= N < =100 1<= M <= 79

 

题解:

做完上下界可行流和最大最小流还不算完,我们还可以搞上下界费用流。。。。。

我们考虑这道题的建模:

由于一共有m个人,所以我们额外建一个节点S‘,连一条S->S',上下界为[m,m],费用为0的弧,这样我们就限制了人数

对于每个国家"有且仅有"的限制,我们把国家拆点为入点i和出点i',对于国家i连一条i->i',上下界为[vi,vi],费用为0的弧,这样就可以限制通过这个国家的人数

当然,由于可以在任意一个地方开始旅行,所以我们还要对于每个国家i连一条S'->i,上下界为[0,inf],费用为0的弧

同理,由于可以在任意一个地方结束旅行,所以我们还要对于每个国家i连一条i'->T,上下界为[0,inf],费用为0的弧

而对于国家i,j间的机票钱,我们需要连一条i'->j,上下界为[0,inf],费用为val[i][j]的弧

在这样构图之后,我们考虑如何处理上下界:

依旧定义totflow数组表示某个点的入流量-出流量,那么我们给totflow[i]>0的点连i->tt的边,给totflow[i]<0的点连ss->i的边

然后我们考虑,最终的总费用应该是附加流的最小费用最大流+初始流的残量网络中每条边的剩余流量*边权

由于每条边不是费用为0,就是上下界残量(上界-下界)为0,所以残量网络带来的费用为0

所以我们只要计算附加流的最小费用最大流输出即可啦!

代码实现:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 inline int min(int a,int b){return a<b?a:b;}
 5 inline int max(int a,int b){return a>b?a:b;}
 6 const int N=210,M=40010,inf=0x7fffffff;
 7 struct edge{int zhong,next,val,flow;}s[M];
 8 int n,m,e=1,adj[N],totflow[N];
 9 int S,T,ss,tt,Station;
10 int hd,tl,q[M],ans,dis[N],pre[N];
11 inline void add(int qi,int zhong,int flow,int val)
12 {
13     s[++e].zhong=zhong,s[e].next=adj[qi],adj[qi]=e,
14     s[e].flow=flow,s[e].val=val;
15 }
16 inline void Add(int a,int b,int c,int d)
17     {add(a,b,c,d),add(b,a,0,-d);}
18 bool vis[N];
19 inline int Shoot()
20 {
21     int x=tt,f=inf;
22     while(x!=ss)
23         f=min(f,s[pre[x]].flow),
24         x=s[pre[x]^1].zhong;
25     x=tt;
26     while(x!=ss)
27         s[pre[x]].flow-=f,s[pre[x]^1].flow+=f,
28         x=s[pre[x]^1].zhong;
29     return f;
30 }
31 inline bool spfa()
32 {
33     memset(dis,0x7f,sizeof(dis));
34     memset(vis,0,sizeof(vis));
35     dis[ss]=0,vis[ss]=1,q[hd=tl=1]=ss;
36     register int i,j,x,u;
37     while(hd<=tl)
38         for(x=q[hd++],vis[x]=0,i=adj[x];i;i=s[i].next)
39             if(s[i].flow&&dis[(u=s[i].zhong)]>dis[x]+s[i].val)
40             {
41                 pre[u]=i,dis[u]=dis[x]+s[i].val;
42                 if(!vis[u])q[++tl]=u,vis[u]=1;
43             }
44     if(dis[tt]==dis[0])return 0;
45     ans+=dis[tt]*Shoot();
46     return 1;
47 }
48 int main()
49 {
50     register int i,j,k,val,lim;
51     scanf("%d%d",&n,&m);
52     Station=2*n+1,S=Station+1,T=S+1,ss=T+1,tt=ss+1;
53     totflow[S]-=m,totflow[Station]+=m;
54     for(i=1;i<=n;++i)
55     {
56         scanf("%d",&lim);
57         totflow[i]-=lim,totflow[n+i]+=lim;
58         Add(Station,i,inf,0),Add(n+i,T,inf,0);
59     }
60     for(i=1;i<n;++i)
61         for(j=i+1;j<=n;++j)
62         {
63             scanf("%d",&val);
64             if(val!=-1)Add(i+n,j,inf,val);
65         }
66     for(i=1;i<=T;++i)
67     {
68         if(totflow[i]>0)Add(ss,i,totflow[i],0);
69         if(totflow[i]<0)Add(i,tt,-totflow[i],0);
70     }
71     Add(T,S,inf,0);
72     while(spfa());
73     printf("%d\n",ans);
74 }

 

posted @ 2017-09-24 20:49  LadyLex  阅读(484)  评论(0编辑  收藏  举报