【无源汇有上下界可行流】ZOJ - 2314 Reactor Cooling

Step1 Problem:

给你 n 个点,m 条流量下界是 low ,上界是 up 的单向边。问你能否满足每时每刻每条边的流量都在 [low, up] 范围内,如果不满足输出 NO, 满足输出 YES 同时输出每条边的流量。

Step2 Ideas:

上下界网络流和平常的网络流不同在于多出了两个点:超级源点 S, 超级汇点 T.
超级源点 S:每条边下界流量都由 S 流出,这样原图就可以变成没有下界的容量为 up-low 的图了。
超级汇点 T:T 是用来判断 流入每个点的流量 是否够 它流出的下界
知道 S, T 的作用后:
例如:一条边 u -> v,下界 low, 上界 up.
那么 S -> v 流入 low,u -> T 流入 low,u-> v 流入 up-low. (这样就没有下界了)
S -> v 流入 low:S 充当了 u 流向 v 的下界
u -> T 流入 low:如果 u 有流量 low 流入 T,代表有流量 low 可以到达 u,那么 u->v 的下界流量肯定能满足
u-> v 流入 up-low:S 充当了 u 流向 v 的下界,所以我的上下界都得减少 low.

对于新图跑 S 到 T 的最大流,如果 T 收到流量是满流的 或者 S 发出去的流量是满流的,就代表所有下界流量都能满足。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int M = 50000;
const int N = 500;
struct node
{
    int to, cap, next;
}Map[M];
int head[N], cnt;
int lsum[N], bl[N*N];
int vis[N], cur[N];
int ans[N*N];
bool bfs(int s, int e)
{
    memset(vis, -1, sizeof(vis));
    queue<int> q;
    q.push(s);
    vis[s] = 0;
    while(!q.empty())
    {
        s = q.front(), q.pop();
        for(int i = head[s]; ~i; i = Map[i].next)
        {
            int to = Map[i].to, cap = Map[i].cap;
            if(cap && vis[to] == -1)
            {
                vis[to] = vis[s] + 1;
                q.push(to);
            }
        }
    }
    if(vis[e] == -1) return 0;
    else return 1;
}
int dfs(int s, int e, int f)
{
    if(s == e) return f;
    int ans = 0;
    for(int &i = cur[s]; ~i; i = Map[i].next)
    {
        int to = Map[i].to, &cap = Map[i].cap;
        if(vis[to] > vis[s] && cap)
        {
            int d = dfs(to, e, min(f, cap));
            if(d)
            {
                cap -= d;
                Map[i^1].cap += d;
                f -= d;
                ans += d;
                if(!f) break;
            }
        }
    }
    if(ans) return ans;
    vis[s] = -1;
    return 0;
}
int dinic(int s, int e)
{
    int ans = 0;
    while(bfs(s, e))
    {
        memcpy(cur, head, sizeof(head));
        ans += dfs(s, e, inf);
    }
    return ans;
}
void init()
{
    cnt = 0;
    memset(head, -1, sizeof(head));
    memset(lsum, 0, sizeof(lsum));
    memset(bl, 0, sizeof(bl));
}
void add(int u, int v, int cap)
{
    Map[cnt] = (node){v, cap, head[u]};
    head[u] = cnt++;
    Map[cnt] = (node){u, 0, head[v]};
    head[v] = cnt++;
}
int main()
{
    int T, n, m, u, v, low, up;
    scanf("%d", &T);
    while(T--)
    {
        init();
        scanf("%d %d", &n, &m);
        int S = 0, T = n+1;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d %d %d %d", &u, &v, &low, &up);
            add(u, v, up-low);
            lsum[v] += low; //流入 v 的下界和
            lsum[u] -= low; //流出 u 的下界和
            bl[i] = low;
        }
        int sum = 0;// S 发出去的流量
        for(int i = 1; i <= n; i++)
        {
            if(lsum[i] > 0) {
                sum += lsum[i];
                add(S, i, lsum[i]);//流入比流出多
            }
            if(lsum[i] < 0) add(i, T, -lsum[i]);//流出比流入多
        }
        if(sum != dinic(S, T)) {//如果最大流不等于发出去的流量,则不满足
            printf("NO\n");
        }
        else {
            printf("YES\n");
            for(int i = 1; i <= m; i++) {//每条边在下界的基础上多流了多少流量
                int id = (i-1)*2;
                ans[i] = Map[id^1].cap;
            }
            for(int i = 1; i <= m; i++)
            {
                printf("%d\n", ans[i]+bl[i]);
            }
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值