这题的第一问是一个裸最大流,不多说了,关键在于第二问。首先有一个结论,Bob一定把费用加在一条边上,于是我们就可以二分每条边的流量,验证是否能得到最大流即可
code:
#include<iostream> #include<cstdio> #include<cstring> #define inf 0x7fffffff using namespace std; int n,m,p,e,ei; int point[101],next[2001]; int cur[101],pre[101],gap[101],lev[101]; struct hp{ int u,v; double c; }a[2001],ai[2001]; void add(int x,int y,double c) { e++; a[e].u=x; a[e].v=y; a[e].c=c; next[e]=point[x]; cur[x]=point[x]=e; e++; a[e].u=y; a[e].v=x; a[e].c=0; next[e]=point[y]; cur[y]=point[y]=e; } void addi(int x,int y,double c) { ei++; ai[ei].u=x; ai[ei].v=y; ai[ei].c=c; ei++; ai[ei].u=y; ai[ei].v=x; ai[ei].c=0; } double ISAP(int vs,int vt) { memset(lev,0,sizeof(lev)); memset(gap,0,sizeof(gap)); memset(pre,0,sizeof(pre)); int i,v,u=vs,minl; double maxt=0,aug; bool f=false; gap[0]=vt-vs+1; while (lev[vs]<vt) { f=false; for (v=cur[u];v!=0;v=next[v]) if (lev[u]==lev[a[v].v]+1&&a[v].c>0) {f=true; cur[u]=v; break;} if (f) { pre[a[v].v]=v; u=a[v].v; if (u==vt) { aug=inf; for (i=v;i!=0;i=pre[a[i].u]) if (a[i].c<aug) aug=a[i].c; maxt+=aug; for (i=v;i!=0;i=pre[a[i].u]) { a[i].c-=aug; a[i^1].c+=aug; } u=vs; } } else { minl=vt; for (i=point[u];i!=0;i=next[i]) if (minl>lev[a[i].v]&&a[i].c>0) minl=lev[a[i].v]; gap[lev[u]]--; if (gap[lev[u]]==0) break; lev[u]=minl+1; cur[u]=point[u]; gap[lev[u]]++; if (u!=vs) u=a[pre[u]].u; } } return maxt; } int main() { int i,x,y; double t,ans,l,r,mid,c,maxn=0; e=ei=1; scanf("%d%d%d",&n,&m,&p); for (i=1;i<=m;++i) { scanf("%d%d%lf",&x,&y,&c); add(x,y,c); addi(x,y,c); maxn=max(maxn,c); } ans=ISAP(1,n); printf("%d\n",(int)ans); l=0; r=maxn; while (r-l>1e-6) { memset(point,0,sizeof(point)); memset(cur,0,sizeof(cur)); memset(next,0,sizeof(point)); e=1; mid=(l+r)/2; for (i=2;i<=2*m+1;++i) if (i%2==0) add(ai[i].u,ai[i].v,min(mid,ai[i].c)); t=ISAP(1,n); if (t==ans) r=mid-(1e-6); else l=mid+(1e-6); } printf("%0.4lf\n",l*p); }