BZOJ1877: [SDOI2009]晨跑
【传送门:BZOJ1877】
简要题意:
给出n个点,起点为1,终点为n,给出m条边,每条边都有费用
每个点只能走一次,请问最多有多少次机会能从起点到终点,并且求出这几次的最小费用和
题解:
一开始还担心有环,实际上没有
最小费用最大流
我们将每个点(除了源点和汇点)都拆成两个点x,y,并且x,y之间的流量为1,费用为0,这样就保证每个点只走一次,并且不影响费用
然后就直接跑费用流,每找到一条增广路就相当于多一次机会,将每条增广路的最小费用加起来即可
空间和小错误卡了很久
参考代码:
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; struct node { int x,y,d,c,next,other; }a[1100000];int len,last[110000]; void ins(int x,int y,int d,int c) { int k1=++len,k2=++len; a[k1].x=x;a[k1].y=y;a[k1].d=d;a[k1].c=c; a[k1].next=last[x];last[x]=k1; a[k2].x=y;a[k2].y=x;a[k2].d=-d;a[k2].c=0; a[k2].next=last[y];last[y]=k2; a[k1].other=k2; a[k2].other=k1; } int d[110000],pre[110000],pos[110000]; bool v[110000]; int list[110000]; int st,ed; bool spfa() { for(int i=st;i<=ed;i++) d[i]=999999999; d[st]=0; memset(v,false,sizeof(v)); v[st]=true; list[1]=st; int head=1,tail=2; while(head!=tail) { int x=list[head]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(a[k].c>0&&d[y]>d[x]+a[k].d) { d[y]=d[x]+a[k].d; pos[y]=x; pre[y]=k; if(v[y]==false) { v[y]=true; list[tail++]=y; } } } head++; v[x]=false; } if(d[ed]==999999999) return false; else return true; } int ans,t; void Flow() { while(spfa()) { t++; ans+=d[ed]; int x; for(x=ed;x!=st;x=pos[x]) { a[pre[x]].c--; a[a[pre[x]].other].c++; } } } int main() { int n,m; scanf("%d%d",&n,&m); len=0;memset(last,0,sizeof(last)); for(int i=1;i<=m;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); if(x==1) ins(x,2*(y-1),d,1); else ins(2*(x-1)+1,2*(y-1),d,1); } for(int i=2;i<n;i++) ins(2*(i-1),2*(i-1)+1,0,1); st=1;ed=2*(n-1); ans=t=0; Flow(); printf("%d %d\n",t,ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚