这题的第一问是一个裸最大流,不多说了,关键在于第二问。首先有一个结论,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);
}