bzoj 1927 [Sdoi2010]星际竞速
最小费用最大流。
首先,由于赛车只能由每个星球飞往引力比它大的星球,所以双向边其实是单向边。由于所有连边都是从小到大连,所以图是一张DAG。
大概就是“最小权路径覆盖”。
将每个点拆点。
源点向每个点的入点连一条容量为1费用为0的边。
源点向每个点的出点连一条容量为1费用为瞬移到该点所需时间的边。
每个点的出点向汇点连一条容量为1费用为0的边。
对于每条边(i,j),从i点入点向j点出点连一条容量为1费用为航路所需时间的边。
构造十分精妙。
考虑由于求最大流,所以所有点的出点向汇点的连边都会流满。每个点的出点的到达方式即为此点的到达方式:
1.由源点流来:指在某一时刻瞬移到该星球。
2.由入点流来:指由其他星球沿航路到该星球。
第二种方式正确性?流到该点出点的某入点对应的星球,在之前的某一时刻一定由某种合法方式达到过,追溯到头一定是某个瞬移到的点(因为图中没有环),“追溯”的过程就是这一条路径。注意为什么源点会向所有入点连费用为0的边,这条边的容量其实对应着这个点的出点到达后而产生的贡献。每个点的入点只连向它能到的出点,又由于图为DAG,所以一定存在必须瞬移(走源点到出点的边)的点,正确性也就显然了。换句话说,每个从源点直接流向出点的点是一条路径的起始点,它的入点因为出点的到达而有意义,之后就是整条路径的递推。
1 #include<cstring> 2 #include<cstdio> 3 #include<algorithm> 4 #include<iostream> 5 #include<queue> 6 using namespace std; 7 const int dian=1605; 8 const int bian=40005; 9 const int INF=0x3f3f3f3f; 10 int h[dian],ver[bian],val[bian],cos[bian],nxt[bian],minn[dian],with[dian]; 11 int d[dian],v[dian]; 12 int n,m,aa,bb,cc,tot; 13 int S,T; 14 void add(int a,int b,int c,int d){ 15 tot++;ver[tot]=b;val[tot]=c;cos[tot]=d;nxt[tot]=h[a];h[a]=tot; 16 tot++;ver[tot]=a;val[tot]=0;cos[tot]=-d;nxt[tot]=h[b];h[b]=tot; 17 } 18 bool tell(){ 19 memset(v,0,sizeof(v)); 20 memset(d,0x3f,sizeof(d)); 21 memset(with,0,sizeof(with)); 22 memset(minn,0x3f,sizeof(minn)); 23 queue<int>q; 24 q.push(S); 25 v[S]=1; 26 d[S]=0; 27 while(!q.empty()){ 28 int x=q.front(); 29 q.pop(); 30 v[x]=0; 31 for(int i=h[x];i;i=nxt[i]){ 32 int y=ver[i]; 33 if(d[y]>d[x]+cos[i]&&val[i]){ 34 d[y]=d[x]+cos[i]; 35 minn[y]=min(minn[x],val[i]); 36 with[y]=i; 37 if(!v[y]){ 38 v[y]=1; 39 q.push(y); 40 } 41 } 42 } 43 } 44 if(d[T]==0x3f3f3f3f) 45 return 0; 46 return 1; 47 } 48 int zeng(){ 49 for(int i=T;i!=S;i=ver[with[i]^1]){ 50 val[with[i]]-=minn[T]; 51 val[with[i]^1]+=minn[T]; 52 } 53 return d[T]*minn[T]; 54 } 55 int dinic_cost(){ 56 int r=0; 57 while(tell()) 58 r+=zeng(); 59 return r; 60 } 61 int main(){ 62 memset(h,0,sizeof(h)); 63 memset(nxt,0,sizeof(nxt)); 64 tot=1; 65 scanf("%d%d",&n,&m); 66 S=2*n+1,T=2*n+2; 67 for(int i=1;i<=n;i++){ 68 scanf("%d",&aa); 69 add(S,i+n,1,aa); 70 } 71 for(int i=1;i<=m;i++){ 72 scanf("%d%d%d",&aa,&bb,&cc); 73 if(aa>bb) 74 aa^=bb^=aa^=bb; 75 add(aa,bb+n,1,cc); 76 } 77 for(int i=1;i<=n;i++){ 78 add(S,i,1,0); 79 add(i+n,T,1,0); 80 } 81 printf("%d",dinic_cost()); 82 return 0; 83 }