3130: [Sdoi2013]费用流

本文介绍了一种结合最大流算法与二分搜索法解决特定问题的方法。问题中,Alice和Bob通过设置最大流和权重来博弈,目标是最小化总费用。文章详细解释了如何利用ISAP算法找到最大流,并通过二分法确定最优权重。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目大意:Alice来弄一条最大流,Bob来给Alice弄好的最大流定权值。
定权值的方法是给没条边定一个值wi,然后wi*flow(i)的和就是总的费用,但要求所有wi之和为p。
现在Bob希望最后的费用最大,即对于每一种最大流方案都会有一种最大的定值方案。
Alice则希望花费最小,即选出一种最大流方案,使得这种方案的最大定值是所有最大流方案中最小的
求最大流,及这个最小的最大定值

题解:第一问裸流,对于第二问,Bob显然把单位费用全部放在流量最大的边上,这样Alice需要选择方案,使得在最大流的前提下流量最大的边尽量小,这个明显单调……,二分mid,将所有边权改为mid,若最大流不减少说明可行

没有保证答案为整数,所以需要实数二分……
这里为了避免实数二分把值都乘以10000

我的收获:忽视题目胡扯……题目似乎扯到了博弈论……避免实数技巧

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
const int M=105;
#define INF 1e17
#define B 10000
 
int n,m,st,ed,t,p;
int head[M],last[M],d[M],num[M];
long long F,ret,mx,z[1005];
bool Exit;
 
struct edge{int to,nex,id;long long c;}e[2005];
 
void add(int u,int v,int I,long long w){e[t].id=I,e[t].to=v,e[t].nex=head[u],e[t].c=w,last[u]=head[u]=t++;}
void insert(int x,int y,int I,long long z){add(x,y,I,z),add(y,x,I,0);};
 
long long dfs(int x,long long in)
{
    if(x==ed) return in;
    long long ans=0,f;
    for(int i=last[x];i!=-1;last[x]=i=e[i].nex)
    {
        int v=e[i].to;
        if(e[i].c&&d[v]==d[x]-1){
            f=dfs(v,min(in-ans,e[i].c));
            ans+=f;e[i].c-=f,e[i^1].c+=f;
            if(Exit||ans==in) return ans;
        }
    }
    if(--num[d[x]]==0) Exit=1;
    d[x]++,num[d[x]]++,last[x]=head[x];
    return ans;
}
 
long long ISAP()
{
    Exit=0;long long flow=0;
    while(!Exit) flow+=dfs(st,INF);
    return flow;
}
 
void build(long long x){
    memset(d,0,sizeof(d));
    memset(num,0,sizeof(num));num[0]=n+1;
    for(int i=0;i<t;i+=2) e[i].c=min(x,z[e[i].id]);
} 
 
void work()
{
    F=ISAP();
    printf("%lld\n",F/B);
    for(long long l=0,r=mx*B;l<=r;){
        long long mid=l+r>>1;
        build(mid);
        if(ISAP()>=F) r=mid-1,ret=mid;
        else l=mid+1;
    }
    printf("%.4lf\n",double(ret)*p/B);
} 
 
void init()
{
    cin>>n>>m>>p;
    st=1,ed=n,num[0]=ed+1;
    memset(head,-1,sizeof(head));
    memset(last,-1,sizeof(last));
    int x,y;
    for(int i=1;i<=m;i++)
        scanf("%d%d%lld",&x,&y,&z[i]),z[i]*=B,insert(x,y,i,z[i]),mx=max(mx,z[i]);
}
 
int main()
{
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值