bzoj 2055 80人环游世界

有源汇上下界最小费用可行流。

将每个国家拆点。

源点向一个新建节点连一条上界为总人数下界为0费用为0的边。

新建节点向每个国家的入点连一条上界为正无穷下界为0费用为0的边。

每个国家的入点向出点连一条上下界均为该国家访问人数费用为0的边。

每个国家的出点向汇点连一条上界为正无穷下界为0费用为0的边。

对于国家i能到国家j,i的出点向j的入点连一条上界为正无穷下界为0费用为机票费的边。

然后对图求一边有源汇上下界最小费用可行流即可得出最小费用。

具体做法是先将所有边的下界乘费用相加,这一部分是必须的费用,然后按照“有源汇上下界可行流”建图,从超级源向超级汇跑一遍最小费用最大流,两个费用相加即为答案。

至于本题本身的建图思路,与bzoj1927星际竞速类似,下面放一份十分良心的题解。

天地良心无与伦比谁与争锋的一篇题解

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
 7 const int dian=305;
 8 const int bian=100005;
 9 const int INF=0x3f3f3f3f;
10 int h[dian],nxt[bian],ver[bian],val[bian],cos[bian];
11 int d[dian],v[dian],with[dian],minn[dian];
12 int in[dian],out[dian];
13 int n,m,tot,aa,mf;
14 int S,T,SS,TT;
15 void add(int a,int b,int c,int d,int e){
16     tot++;ver[tot]=b;val[tot]=d-c;cos[tot]=e;nxt[tot]=h[a];h[a]=tot;
17     tot++;ver[tot]=a;val[tot]=0;cos[tot]=-e;nxt[tot]=h[b];h[b]=tot;
18     in[b]+=c,out[a]+=c;
19     mf+=c*e;
20 }
21 bool tell(){
22     memset(v,0,sizeof(v));
23     memset(d,0x3f,sizeof(d));
24     memset(with,0,sizeof(with));
25     memset(minn,0x3f,sizeof(minn));
26     queue<int>q;
27     q.push(S);
28     v[S]=1;
29     d[S]=0;
30     while(!q.empty()){
31         int x=q.front();
32         q.pop();
33         v[x]=0;
34         for(int i=h[x];i;i=nxt[i]){
35             int y=ver[i];
36             if(d[y]>d[x]+cos[i]&&val[i]){
37                 d[y]=d[x]+cos[i];
38                 minn[y]=min(minn[x],val[i]);
39                 with[y]=i;
40                 if(!v[y]){
41                     v[y]=1;
42                     q.push(y);
43                 }
44             }
45         }
46     }
47     if(d[T]==0x3f3f3f3f)
48         return false;
49     return true;
50 }
51 int zeng(){
52     for(int i=T;i!=S;i=ver[with[i]^1]){
53         val[with[i]]-=minn[T];
54         val[with[i]^1]+=minn[T];
55     }
56     return minn[T]*d[T];
57 }
58 int dinic_cost(){
59     int r=0;
60     while(tell())
61         r+=zeng();
62     return r;
63 }
64 int main(){
65     memset(h,0,sizeof(h));
66     memset(nxt,0,sizeof(nxt));
67     memset(in,0,sizeof(in));
68     memset(out,0,sizeof(out));
69     tot=1,mf=0;
70     scanf("%d%d",&n,&m);
71     SS=n+n+2,TT=n+n+3,S=n+n+4,T=n+n+5;
72     add(SS,n+n+1,0,m,0);
73     for(int i=1;i<=n;i++)
74         add(n+n+1,i,0,INF,0);
75     for(int i=1;i<=n;i++)
76         add(n+i,TT,0,INF,0);
77     for(int i=1;i<=n;i++){
78         scanf("%d",&aa);
79         add(i,n+i,aa,aa,0);
80     }
81     for(int i=1;i<n;i++)
82         for(int j=1;j<=n-i;j++){
83             scanf("%d",&aa);
84             if(aa!=-1)
85                 add(n+i,i+j,0,INF,aa);
86         }
87     add(TT,SS,0,INF,0);
88     for(int i=1;i<=TT;i++){
89         if(in[i]>out[i])
90             add(S,i,0,in[i]-out[i],0);
91         else if(out[i]>in[i])
92             add(i,T,0,out[i]-in[i],0);
93     }
94     printf("%d",mf+dinic_cost());
95     return 0;
96 }

 注意题意叙述不清,每个人可以从任意一个国家开始走。

转载于:https://www.cnblogs.com/dugudashen/p/6270280.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值