bzoj 1927 星际竞速 —— 最小费用最大流
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1927
首先注意到这是个DAG;
考虑每个点从哪里来,可以是瞬移来的,也可以是从某个点走过来的,而从每个点走出去只能用一次;
所以拆点,i 表示从这个点走出去,n+i 表示来到这个点;
建图:
1.瞬移:S 向 n+i 连边权 a[i],流量1的边
2.走过来:如果 i 能走到 j,那么 i 向 n+j 连边权 w,流量1的边
然后 S 向 i 连边权0,流量1的边,表示一个点只能走出去一次;
n+i 向 T 连边权0,流量1的边,表示一个点只能到达一次;
因为是DAG,所以不会有环,而且一定会有一个点瞬移开始;
建图真妙啊!
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int const xn=1605,xm=4e4+5,inf=0x3f3f3f3f; int n,m,S,T,hd[xn],ct=1,to[xm],nxt[xm],w[xm],c[xm],dis[xn],inc[xn],pre[xn]; bool vis[xn]; queue<int>q; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;} void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,0);} bool bfs() { memset(dis,0x3f,sizeof dis); dis[S]=0; q.push(S); vis[S]=1; inc[S]=inf;//inc while(q.size()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=hd[x],u;i;i=nxt[i]) if(dis[u=to[i]]>dis[x]+w[i]&&c[i]) { dis[u]=dis[x]+w[i]; pre[u]=i; inc[u]=min(inc[x],c[i]); if(!vis[u])vis[u]=1,q.push(u); } } return dis[T]!=inf; } void upd() { int x=T; while(x!=S) { int i=pre[x]; c[i]-=inc[T]; c[i^1]+=inc[T]; x=to[i^1]; } } int main() { n=rd(); m=rd(); S=0; T=(n<<1)+1; for(int i=1,x;i<=n;i++) x=rd(),add(S,i,0,1),add(S,n+i,x,1),add(n+i,T,0,1); for(int i=1,x,y,z;i<=m;i++) { x=rd(); y=rd(); z=rd(); if(x>y)swap(x,y); add(x,n+y,z,1);//n+y } int ans=0; while(bfs())ans+=dis[T]*inc[T],upd(); printf("%d\n",ans); return 0; }