BZOJ 3698 XWW的难题

本文介绍了上下界最大流算法的实现方法,包括如何构造网络流图、填充下界及求解上下界最大流的过程。并通过一道具体题目展示了算法的应用,特别注意矩阵中可能存在的负数处理。

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

上下界最大流

好久没写上下界的网络流了,赶快复习一遍。对于这道题建图不难,就是把行、列当成点,一个连S,一个连T。一个格子当成行到列的边,上下取整当成上下界即可。

先说一下上下界可行流怎么搞。我们只要考虑把下界填平使得图流量平衡即可。对于入下界大于出下界的点,因为要填平下界,而出去的流量少了,因此要给它补充一些流量,S’向它连差值的边即可,反之连T’。

然后考虑上下界最大流怎么搞。一个办法是按照论文里说的二分出一个答案,T->S的连这个权值的边,然后判有没有可行流。正确性易证,但是复杂度带一个log。

一个更高效的算法是先找出原图的可行流,然后直接从S到T再跑最大流即可得到答案。因为可行流保证了在有下界情况下的流量平衡,因此暴力增广是对的。

最后每一条边的下界+流量就是这条边在原图的真实流量。

然后这题有点坑,矩阵里可以有负数,直接用int来取整是向0取整的!……因此要用floor和ceil,坑了半天好气啊……

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 222
using namespace std;
namespace runzhe2000
{
    typedef double db;
    const int INF = 1<<29;
    int S, T, SS, TT, s, t, q[N], level[N];
    int ecnt = 1, n, nn, last[N], cur[N], deg[N], val[N][N];
    db a[N][N];
    struct edge{int next, to, flow;}e[N*N<<2];
    void addedge(int a, int b, int down, int up)
    {
//      printf("%d %d %d %d\n",a,b,down,up);
//      printf("%d %d %d\n",a,b,up-down);
        deg[a] -= down; deg[b] += down;
        e[++ecnt] = (edge){last[a], b, up-down};
        last[a] = ecnt;
        e[++ecnt] = (edge){last[b], a, 0};
        last[b] = ecnt;
    }
    int bfs()
    {
        memset(level,0,sizeof(level)); level[q[0] = s] = 1;
        for(int head=0, tail=1; head<tail; head++)
        {
            int x = q[head];
            for(int i = last[x]; i; i = e[i].next)
            {
                int y = e[i].to;
                if(!level[y] && e[i].flow)
                {
                    level[y] = level[x] + 1, q[tail++] = y;
                    if(y == t) return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int x, int flow)
    {
        if(x == t) return flow; int use = 0;
        for(int &i = cur[x]; i; i = e[i].next)
        {
            int y = e[i].to; if(level[x]+1 != level[y]) continue;
            int w = dfs(y, min(flow-use, e[i].flow));
            e[i].flow -= w; e[i^1].flow += w; use += w;
            if(use == flow) return use;
        }
        return use;
    }
    int dinic(){int r = 0; for(; bfs(); memcpy(cur, last, sizeof(cur)), r += dfs(s, INF)); return r;}
    void main()
    {
        scanf("%d",&n); S = n+n+1, T = n+n+2, SS = n+n+3, TT = n+n+4; nn = n+n+4;
        for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%lf",&a[i][j]);
        for(int i = 1; i <  n; i++) for(int j = 1; j <  n; j++) addedge(i, j+n, (int)floor(a[i][j]), (int)ceil(a[i][j]));
        for(int i = 1; i <  n; i++) addedge(S, i, (int)floor(a[i][n]), (int)ceil(a[i][n])), addedge(i+n, T, (int)floor(a[n][i]), (int)ceil(a[n][i]));
        for(int i = 1; i <= nn; i++) deg[i] > 0 ? addedge(SS, i, 0, deg[i]) : addedge(i, TT, 0, -deg[i]);
        int v = 0; for(int i = last[SS]; i; i = e[i].next) v += e[i].flow;
        addedge(T, S, 0, INF); s = SS, t = TT; if(dinic() != v) return (void)puts("No");
        for(int i = last[S]; i; i = e[i].next) if(e[i].to == T) e[i].flow = e[i^1].flow = 0;//
        int ans = 0; s = S, t = T; dinic();
        for(int i = 1; i < n; i++)
            for(int j = last[i]; j; j = e[j].next) if(n < e[j].to && e[j].to <= n+n)
                ans += (int)floor(a[i][e[j].to-n]) + e[j^1].flow, val[i][e[j].to - n] = e[j^1].flow;
        printf("%d\n",ans*3);
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值