1. 程式人生 > >bzoj 1927 [Sdoi2010]星際競速——網路流

bzoj 1927 [Sdoi2010]星際競速——網路流

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=1927

每個點拆點保證只經過一次。

主要是如果經過了這個點,這個點應該向匯點流過去表示經過了它。但這樣就難以表示它接著往別的點走了。

發現是DAG。而且每個點都會要求經過。所以不妨認為連向它的點一定是已經經過了的點。

源點向每個點的入點連容量為1的邊,表示走到這個點之後只能選擇一個孩子走過去。從連向自己的點的入點向自己的出點連邊,表示它能讓自己“被經過”即流向匯點。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include
<queue> using namespace std; const int N=1605,M=34805,INF=N; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;}
int n,m,t,hd[N],xnt=1,nxt[M],to[M],cap[M],w[M]; int dis[N],pre[N],info[N],ans; bool ins[N]; queue<int> q; void add(int x,int y,int d) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=d; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-d; } bool spfa() { memset(ins,0,sizeof ins); memset(dis,
0x3f,sizeof dis); q.push(0);ins[0]=1;dis[0]=0;info[0]=INF;info[t]=0; while(q.size()) { int k=q.front();q.pop();ins[k]=0; for(int i=hd[k],v;i;i=nxt[i]) if(cap[i]&&dis[v=to[i]]>dis[k]+w[i]) { dis[v]=dis[k]+w[i]; pre[v]=i;info[v]=Mn(info[k],cap[i]); if(!ins[v])q.push(v),ins[v]=1; } } return info[t]; } void ek() { int s=info[t]; for(int i=pre[t];i;i=pre[to[i^1]]) { ans+=s*w[i];cap[i]-=s;cap[i^1]+=s; } } int main() { n=rdn();m=rdn();t=(n<<1)+1; for(int i=1,j=n+1,d;i<=n;i++,j++) d=rdn(),add(0,i,0),add(0,j,d),add(j,t,0); for(int i=1,u,v,d;i<=m;i++) { u=rdn();v=rdn();d=rdn(); if(u>v)swap(u,v); add(u,v+n,d); } while(spfa())ek(); printf("%d\n",ans); return 0; }