SGU 194/LOJ 115(无源汇可行流模板)

题意:

给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流躺物质。

并且满足每根pipe一定的流量限制,范围为[Li,Ri].即要满足每时刻流进来的不能超过Ri(最大流问题),同时最小不能低于Li。

例如:

46(4个点,6个pipe)
12 1 3 (1->2上界为3,下界为1)
23 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3

这里写图片描述

(LOJ 115更加直白,要求是一样的)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
struct edge{
    int to,cap,rev,id;
};
vector<edge>G[210];
int level[205],iter[205],cha[205],lower[200005],cur[205];
void addedge(int from,int to,int cap)
{
    G[from].push_back(edge{to,cap,(int)G[to].size()});
    G[to].push_back(edge{from,0,(int)G[from].size()-1});//反向容量为0!!
}
void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int>que;
    level[s]=0;que.push(s);
    while(!que.empty()){
        int t=que.front();que.pop();
        for(int i=0;i<G[t].size();i++){
            edge e=G[t][i];
            if(e.cap>0&&level[e.to]<0){
                level[e.to]=level[t]+1;
                que.push(e.to);
            }
        }
    }
}
int dfs(int v,int t,int f)
{
    if(v==t)return f;
    for(int&i=iter[v];i<G[v].size();i++){//注意传引用!
        edge&e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;//不要漏了这个,很多时候可能是无法增广的
}
int maxflow(int s,int t){
    int flow=0;
    for(;;){
        bfs(s);
        if(level[t]<0)return flow;
        memset(iter,0,sizeof(iter));
        int f;
        while(f=dfs(s,t,0x7f7f7f7f))
            flow+=f;
    }
}
struct ans{
    int id,cap;
    bool operator<(const ans v)const{
        return id<v.id;
    }
};
int main() {
    int n, m, i, j, k;
    cin >> n >> m;
    for (i = 1; i <= m; i++) {
        int a, b, d;
        scanf("%d%d%d%d", &a, &b, &lower[i], &d);//每条边的下界
        cur[b] += lower[i];//统计每个点的下界情况
        cur[a] -= lower[i];
        addedge(a, b, d - lower[i]);
        G[a].back().id = i;//记录边的id
    }
    int sum = 0;
    for (i = 1; i <= n; i++) {
        if (cur[i] > 0) {
            addedge(0, i, cur[i]);
            sum += cur[i];
        } else if (cur[i] < 0) {
            addedge(i, 201, -cur[i]);
        }
    }
    int flow = maxflow(0, 201);
    if (flow != sum) {
        cout << "NO" << endl;
        return 0;
    }
    cout << "YES" << endl;
    vector<ans> as;//存答案
    for (i = 1; i <= n; i++) {
        for (j = 0; j < G[i].size(); j++) {
            edge e = G[i][j];
            if (!e.id)continue;//注意反向边以及连向汇点源点的边是没有ID的,也就是ID是0;
            as.push_back(ans{e.id, G[e.to][e.rev].cap + lower[e.id]});//这条边的反向边的容量+这条边的下界
        }
    }
    sort(as.begin(), as.end());
    for (i = 0; i < as.size(); i++) {
        printf("%d\n", as[i].cap);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值