1. 程式人生 > >BZOJ 1927 SDOI2010 星際競速 最小費用最大流

BZOJ 1927 SDOI2010 星際競速 最小費用最大流

  中文題面,還特長。請各位自行品嚐→星際競速

 

分析:

  首先拆穿題面一個偽裝。題面裡竟然明擺著說是雙向圖,後來又變成了“只能由每個星球飛往引力比它大的星球”,所以這就是出題人湊字數的把戲,我們直接將其看作單向邊就好了(不過如果給的順序反了要swap一下)

  這個題目的圖比較好建,比起那些奇奇怪怪的轉化,我們只需要按著題面建圖就好了。

  我們要滿足“每個點恰好訪問一次”這個限制,在求最小費用的題目裡,我們應該用最大流來保證這種限制(就是說,當全圖為最大流時,即為訪問每個點恰好一次)

  我們按照在這個思路去建圖。將每個星球拆成一個入點和一個出點

  從原點S向每個星球的入點連一條容量為1費用為0的邊。

  從原點向每個點的出點連一條容量為1費用為瞬移定位時間的邊(表示不走尋常路的流?)

  假如在原圖中存在(x->y)的一條邊,那麼我們從x的入點向y的出點連容量為1費用為航路所需時間的邊(表示從x點走向了y點完成了這條航道的使命?)

  最小費用最大流,輸出費用,結束。

程式碼:

 1 #include<bits/stdc++.h>
 2 #define ms(a,x) memset(a,x,sizeof(a))
 3 using namespace std;
 4 const int N=5000,M=100000;
 5 const int inf=0x3f3f3f3f
; 6 queue<int>q;int c1[N],c2[N],cnt; 7 struct node{int y,z,f,nxt;}e[M]; 8 int lst[N],pre[N],S,T;bool vis[N]; 9 int h[N],c=1,n,m,d[N],f[N],ans,tot; 10 void add(int x,int y,int l,int z){ 11 e[++c]=(node){y,z,l,h[x]};h[x]=c; 12 e[++c]=(node){x,-z,0,h[y]};h[y]=c; 13 } bool spfa(){ 14 for
(int i=S;i<=T;i++) pre[i]=-1, 15 f[i]=inf,lst[i]=vis[i]=0,d[i]=inf; 16 q.push(S);d[S]=0;pre[S]=0; 17 while(q.size()){ 18 int x=q.front();q.pop();vis[x]=0; 19 for(int i=h[x],y;~i;i=e[i].nxt) 20 if(d[y=e[i].y]>d[x]+e[i].z&&e[i].f){ 21 d[y]=d[x]+e[i].z;pre[y]=x;lst[y]=i; 22 f[y]=min(f[x],e[i].f); 23 if(!vis[y]) vis[y]=1,q.push(y); 24 } 25 } return (pre[T]!=-1); 26 } void solve(){ 27 while(spfa()){ 28 ans+=d[T]*f[T];int x=T; 29 while(x) e[lst[x]].f-=f[T], 30 e[lst[x]^1].f+=f[T],x=pre[x]; 31 } return ; 32 } int main(){ ms(h,-1); 33 scanf("%d%d",&n,&m);S=0;cnt=0; 34 for(int i=1;i<=n;i++) 35 c1[i]=++cnt,c2[i]=++cnt;T=++cnt; 36 for(int i=1,x;i<=n;i++) 37 scanf("%d",&x),add(S,c2[i],1,x); 38 for(int i=1;i<=n;i++) add(S,c1[i],1,0); 39 for(int i=1;i<=n;i++) add(c2[i],T,1,0); 40 for(int i=1,x,y,z;i<=m;i++){ 41 scanf("%d%d%d",&x,&y,&z);if(x>y) 42 swap(x,y);add(c1[x],c2[y],1,z); 43 } solve(); 44 printf("%d\n",ans);return 0; 45 }
費用流