【SDOI 2013】【BZOJ 3130】费用流

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值