P1344 [USACO4.4]追查坏牛奶Pollutant Control

这是一道关于网络流算法的应用题目,通过构建以卡车路线为边、仓库为点的图,求解最小割来确定公司与零售商之间的不连通状态。第一问利用最大流等于最小割的原理,通过Dinic算法直接求解。对于第二问,通过巧妙地附加信息到边权上,不影响最小割大小的同时,解决了求最小割边集的问题,同样基于网络流理论解决。
摘要由CSDN通过智能技术生成

一道网络流初学者的好题。

首先,我们发现以卡车的路线为边,仓库为点,构成了一张图。

然后我们又看到需要花最小代价使公司和零售商不连通。

那么我们很容易得到此题的模型:

以公司为源点,零售商为汇点,求出图中的最小割大小、最小割边数。

第一问,直接上著名的定理:

最大流 = 最小割 \text{最大流}=\text{最小割} 最大流=最小割

任何学过网络流的人都应该知道吧

于是直接上 D i n i c Dinic Dinic秒掉第一问。

然而我们发现第二问有些棘手:似乎没有快速求最小割边集的算法?

别急,我们依然从那个著名的定理入手。

最大流 = 最小割 \text{最大流}=\text{最小割} 最大流=最小割

显而易见的是,当最小割中所有边权均为 1 1 1时,最小割的大小等于最小割的边数。

但此题边权并不是 1 1 1,所以感性理解一下,要想办法把 1 1 1在不影响第一问答案的情况下附加到边上。

而附加信息其实有套路:

当我们使用 p b _ d s pb\_ds pb_ds中的平衡树时,为了实现重复元素的功能,我们将数据存在高位,而低位存一个时间戳。

于是新技能 g e t get get:将原数据 x x x乘一个常数 P P P,再加上附加数据 y y y,就得到新数据 x P + y xP+y xP+y。只要 P > y P>y P>y,原数据就与附加数据8848互不干扰。之后拿 P P P整除、对 P P P取模就可以分离信息。由于附加信息较小,不会对原来的答案造成影响。

于是此题第二问也可以秒掉了。。。

代码 (自我感觉写得挺好看的,喜欢可以拿去)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

#define int long long//注意边数乘边权会爆int

using std::queue;

const int N=1005,M=2005;

int head[N],level[N],cur[N];

struct Edge
{
    int next,to,c;
};
Edge E[M<<1];
void __add(int u,int v,int c)
{
    static int tot=-1;
    E[++tot].next=head[u];
    E[tot].to=v;
    E[tot].c=c;
    head[u]=tot;
}

bool bfs(int s,int t)
{
    memset(level,0x00,sizeof(level));
    queue<int> q;
    q.push(s);
    level[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];~i;i=E[i].next)
        {
            int v=E[i].to;
            if(level[v]||E[i].c==0)continue;
            level[v]=level[u]+1;
            q.push(v);
        }
    }
    return level[t];
}

int dfs(int u,int t,int flow)
{
    if(u==t||flow==0)return flow;
    int now=flow;
    for(int i=cur[u];~i;cur[u]=i=E[i].next)
    {
        int v=E[i].to;
        if(level[v]!=level[u]+1||E[i].c==0)continue;
        int f=dfs(v,t,std::min(now,E[i].c));
        now-=f;
        E[i].c-=f;
        E[i^1].c+=f;
        if(now==0)break;
    }
    return flow-now;
}

int dinic(int s,int t)
{
    int ans=0;
    while(bfs(s,t))
    {
        memcpy(cur,head,sizeof(cur));
        ans+=dfs(s,t,0x7fffffffffffffff);//无穷大别开小了,会死循环
    }
    return ans;
}

signed main()
{
    memset(head,0xff,sizeof(head));
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=0;i<m;++i)
    {
        int u,v,c;
        scanf("%lld%lld%lld",&u,&v,&c);
        __add(u,v,c*(m*2+1)+1);
        __add(v,u,0);
    }
    int res=dinic(1,n);
    printf("%lld %lld",res/(m*2+1),res%(m*2+1));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值