BZOJ1927 SDOI2010 星际竞速 费用流
题意:给定一张有向图,边均由编号较大的点连向编号较小的点,每个点都有一个权值a,表示可以从任意一个点转移到i点,花费a[i]的费用。求遍历每个点一次且仅一次的最小费用,起点终点任意,保证有解。
题解:拆点,费用流决定经过的点的顺序,由于瞬移到i的费用与之前经过的点无关,所以可以直接从S向每个点i连流量为1费用为a[i]的边。
#include <cstdio> #include <climits> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <deque> using namespace std; #define S 0 #define T (2*N+1) #define INF 1061109567 const int MAXN=2000+6; const int MAXM=40000+6; struct Hash{ int u; Hash *Next; Hash(){} Hash(int _u,Hash *_Next):u(_u),Next(_Next){} }*Tab[MAXN],Mem[MAXM],*From[MAXN]; struct Edge{ int u,v,c,w; Edge(){} Edge(int _u,int _v,int _c,int _w):u(_u),v(_v),c(_c),w(_w){} }e[MAXM]; int N,M,cnt,d[MAXN]; bool Flag[MAXN]; deque<int> q; void Insert(int u,int v,int c,int w){ Tab[u]=&(Mem[cnt]=Hash(cnt,Tab[u])),e[cnt++]=Edge(u,v,c,w); Tab[v]=&(Mem[cnt]=Hash(cnt,Tab[v])),e[cnt++]=Edge(v,u,0,-w); } bool SPFA(int s,int t){ memset(d,0X3F,sizeof(d)); d[s]=0,Flag[s]=1,q.push_back(s); int x; while(!q.empty()){ x=q.front(),q.pop_front(); for(Hash *p=Tab[x];p;p=p->Next) if(e[p->u].c && d[e[p->u].v]>d[x]+e[p->u].w){ d[e[p->u].v]=d[x]+e[p->u].w,From[e[p->u].v]=p; if(Flag[e[p->u].v]) continue; Flag[e[p->u].v]=1; if(!q.empty() && d[e[p->u].v]<d[q.front()]) q.push_front(e[p->u].v); else q.push_back(e[p->u].v); } Flag[x]=0; } return d[t]<INF; } int Calc(int s,int t){ int x=INF,Ret=0; for(Hash *p=From[t];p;p=From[e[p->u].u]) x=min(x,e[p->u].c); for(Hash *p=From[t];p;p=From[e[p->u].u]) e[p->u].c-=x,e[p->u^1].c+=x,Ret+=x*e[p->u].w; return Ret; } int MCMF(int s,int t){ int Ans=0; while(SPFA(s,t)) Ans+=Calc(s,t); return Ans; } int main(){ scanf("%d %d",&N,&M); for(int i=1,a;i<=N;i++){ scanf("%d",&a); Insert(S,N+i,1,a); } for(int i=1,u,v,w;i<=M;i++){ scanf("%d %d %d",&u,&v,&w); if(u>v) swap(u,v); Insert(u,N+v,1,w); } for(int i=1;i<=N;i++) Insert(N+i,T,1,0),Insert(S,i,1,0); cout << MCMF(S,T) << endl; return 0; }