hdu4807(最小费用最大流)

题意:给定n点m边有向图,每条边同时可以承载若干人同时经过,每个人的速度均为单位时间通过一条边。K个人从0点开始走向n-1点,问最后一个人走到n-1点的最早时间
解法:这道题目需要完全理解最大流最小费用流的原理:每次得到一条费用最小的增广路
容易得到对于每条增广路来说,我们单位时间向其路中传输a人(a为该增广路流量)最为合理。由增广路时间递增可知,消耗时间是当前增广路所用时间,当前已经运送的人总数可以通过逐次叠加得到。当总人数刚刚超过k时,返回即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define PII pair<int,int>
#include<map>
#include<vector>
#define MP make_pair
#include<map>
#include<bitset>
#include<math.h>
#define ll unsigned long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define X first
#define Y second
#define MP make_pair
using namespace std;
typedef pair<ll,int> PLI;

const int INF = 0X3f3f3f3f;
const int maxn = 5000+10;
int n,m,k;
struct{
    int u,v,nxt,cost,flow,cap;
}e[maxn<<2];
int head[maxn],tot;
int ans;//已用时间
int now;//当前人数
int maxx;//之前增广路径的最长值
int sum;//当前单位时间总流量
int p[maxn<<2];

void add(int u,int v,int cost,int cap){
    e[tot].u=u,e[tot].v=v,e[tot].nxt=head[u],e[tot].cost=cost,e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}

void spfa(int S,int T,int d[]){
    bool vis[maxn];
    queue<int> q;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++) d[i]=INF;
    d[S]=0;
    q.push(S);
    while(!q.empty()){
        int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){
                d[v]=d[u]+e[i].cost;
                p[v]=i;
                if(!vis[v]){ vis[v]=1; q.push(v); }
            }
        }
    }
}

void mcmf(int S,int T){
    int c=0,f=0;
    while(1){
        int d[maxn];
        spfa(S,T,d);
        if(d[T]==INF) break;
        int a=INF;//最小流量
        for(int u=p[T];u!=-1;u=p[e[u].u]){
            a=min(a,e[u].cap-e[u].flow);
        }
        for(int u=p[T];u!=-1;u=p[e[u].u]){
            e[u].flow+=a;
            e[u^1].flow-=a;
        }
        if((d[T]-maxx)*sum+now>=k){
            ans=ceil((1.0*(k-now))/(sum*1.0))+maxx;
            now=k;
            return;
        }
        else if(now+(d[T]-maxx)*sum+a>=k){
            ans=d[T];
            now=k;
            return;
        }
        else now+=((d[T]-maxx)*sum+a);
        sum+=a,maxx=max(maxx,d[T]);
    }
}
int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        memset(head,-1,sizeof(head)); tot=0;
        memset(p,-1,sizeof(p));
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,1,z),add(y,x,-1,0);
        }
        if(!k){
            printf("0\n");
            continue;
        }
        ans=now=maxx=sum=0;
        mcmf(0,n-1);
        if(!now) printf("No solution\n");
        else{
            if(now>=k) printf("%d\n",ans);
            else{
                ans=ceil((1.0*(k-now))/(sum*1.0))+maxx;
                printf("%d\n",ans);
            }

        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值