bzoj1927: [Sdoi2010]星际竞速
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1927
思路:拆点拆成x和x'
S向x'连边,容量为1,费用为定位时间
S向x连边,容量为1,费用为0
对于原图的边u->v
u向v‘连边,容量为1,费用为时间
x’向T连边,容量为1,费用为0;
跑一遍费用流即可
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=1610,maxm=200010; using namespace std; int n,m,A[maxn],pre[maxm],now[maxn],son[maxm],val[maxm],cost[maxm],from[maxm],tot=1; int S=maxn-2,T=maxn-1,dis[maxn],q[maxm+10],head,tail,last[maxn],minc,flow; bool bo[maxn]; struct Edge{int x,y,z;}E[maxm]; void add(int a,int b,int c,int d){pre[++tot]=now[a],now[a]=tot,from[tot]=a,son[tot]=b,val[tot]=c,cost[tot]=d;} void ins(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);} int p0(int x){return x<<1;} int p1(int x){return (x<<1)|1;} bool spfa(){ memset(dis,63,sizeof(dis));int inf=dis[0]; memset(bo,0,sizeof(bo)); q[tail=1]=S,dis[S]=head=0; while (head!=tail){ if (++head>maxm) head=1; int x=q[head]; for (int y=now[x];y;y=pre[y]) if (val[y]>0&&dis[son[y]]>dis[x]+cost[y]){ dis[son[y]]=dis[x]+cost[y],last[son[y]]=y; if (!bo[son[y]]){ if (++tail>maxm) tail=1; q[tail]=son[y],bo[son[y]]=1; } } bo[x]=0; } return dis[T]<inf; } void find(){ int lim=1e9; for (int x=T,y;x!=S;x=from[y]) y=last[x],lim=min(lim,val[y]); flow+=lim; for (int x=T,y;x!=S;x=from[y]) y=last[x],minc+=lim*cost[y],val[y]-=lim,val[y^1]+=lim; } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&A[i]); for (int i=1,x,y,z;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); if (x>y) swap(x,y); E[i]=(Edge){x,y,z}; } for (int i=1;i<=n;i++) ins(S,p1(i),1,A[i]),ins(S,p0(i),1,0),ins(p1(i),T,1,0); for (int i=1;i<=m;i++) ins(p0(E[i].x),p1(E[i].y),1,E[i].z); while (spfa()) find(); //spfa(); //for (int i=1;i<=n;i++) printf("%d\n",from[last[i]]); printf("%d\n",minc); return 0; }