有源汇上下界最小费用可行流。
将每个国家拆点。
源点向一个新建节点连一条上界为总人数下界为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 }
注意题意叙述不清,每个人可以从任意一个国家开始走。