hud4807 Lunch Time 【费用流 求前k条不相交的最短路】

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4807

题意:一个n个点的,m条边的有向图,有k个学生在点0,他们要去点n-1,每条边的长度都是1,容量为w,现在问你,这k个学生最少要多少时间到n-1.

分析:很明显,时间最少肯定走最短路,但是每条边都有容量,所以有的学生走一条最短路的时候其他学生会去走其他的路,这些路都是不相交的,按照贪心策略我们走的时前k条不相交的最短路,费用流有个性质就是每次都会找到的最小费用就是且他们的流量是不相交的,就是我们要求的东西。

现在我们可以知道所有最短路了,它们需要的时间是递增的,开始贪心。

我们要求通过第x条路的时候,可以算出所有人通过前x条路的时间,然后去最小的时间。

计算过程:我们可以得到前x条每单位时间通过多少人(就是这些路的容量),时间=剩下的人/现在每单位时间通过的人数+当前道路时间

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 3010
#define Mm 20005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CLRS(a,b,Size) memset((a),(b),sizeof((a[0]))*(Size+1))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
int dis[Mn],pre[Mn],head[Mn];
bool vis[Mn];
int tot;
struct edge {
    int v,cap,next,cost;
} e[Mm];
void addedge(int u,int v,int w,int cost) {
    e[tot].v=v;
    e[tot].cap=w;
    e[tot].cost=cost;
    e[tot].next=head[u];
    head[u]=tot++;
}
queue<int>q;
bool spfa(int s,int en) {
    while(!q.empty()) q.pop();
    for(int i=s ; i<=en; i++) {
        dis[i]=INF;
        vis[i]=false;
        pre[i]=-1;
    }
    q.push(s);
    dis[s]=0;
    vis[s]=true;
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u]; i!=-1; i=e[i].next) {
            int v=e[i].v;
            int cost=e[i].cost;
            if(e[i].cap&&dis[v]>dis[u]+cost) {
                dis[v]=dis[u]+cost;
                pre[v]=i;
                if(!vis[v]) {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    if(dis[en]==INF) return false;
    return true;
}
int sum,bfTime,num,preSeNum;
void addmaxflow(int t) {
    int maxflow=INF;
    for(int i=pre[t]; i!=-1; i=pre[e[i^1].v]) maxflow=min(maxflow,e[i].cap);
    for(int i=pre[t]; i!=-1; i=pre[e[i^1].v]) {
        e[i].cap-=maxflow;
        e[i^1].cap+=maxflow;
    }
    num=max(num-(dis[t]-bfTime)*preSeNum-maxflow,0);
    preSeNum+=maxflow;
    int needTime=dis[t]+(int)ceil(1.0*num/preSeNum);
    sum=min(sum,needTime);
    bfTime=dis[t];
}
void init() {
    tot=0;
    CLR(head,-1);
    preSeNum=0;
    sum=INF;
    bfTime=0;
}
void get(int s,int t) {
    while(spfa(s,t)) {
        addmaxflow(t);
        if(num==0) break;
    }
    if(sum==INF) printf("No solution\n");
    else printf("%d\n",sum);
}
int main() {
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)) {
        init();
        int s=0,t=n-1;
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w,1);
            addedge(v,u,0,-1);
        }
        if(k==0) {
            printf("0\n");
            continue;
        }
        num=k;
        get(s,t);
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值