****OJ - 1**7 B***d (费用流)

****OJ - 1**7 B***d (费用流)

题意

某国防御系统需要保证节点1和节点n保持联通,每条路有一个防卫等级的属性,初始都为0,最高限制为 m a x i max_i maxi,每提高一级需要花费 c o s t i cost_i costi,敌国每破坏一条路都要花费该路的等级那么多的成本,该国国防预算F,问最优防御策略下敌国需要多少钱才能使得该国防御系统瘫痪(使得1节点和n节点不连通)

思路

这题怎么看出来是网络流呢?我们可以把每条边的等级看成流量,每条边的等级上限看成容量,提高一级的花费就是费用了。每条路径破坏的最低成本就是该路径上等级最低的边。所以我们可以用费用流求该路径上的最小的边(找增广路的过程)。如果预算不足,那么就只能把预算平摊到该路径上的所有边。其实如果预算充足,最后求到的就是最小割。需要注意的是这题是无向图,一定要加反向的边,虽然防御联络的时候只会从1走到n,但是谁能保证题目不把边反着给呢?我这么wa了20发,疯狂在找自己费用流所谓不存在的bug,还好凉凉把我点醒了。比赛的时候因为G题的no response,卡死了G 的特判没空写这题(估计有空也是死在无向图这个坑上,毕竟做到的网络流题目基本都是有向图,我会想当然)。

因为官方重现没有放出,所以对题目进行了和谐处理,如果官方重现出了我就回来把屏蔽去掉(不鸽的话)

这可能是我最后一篇题解博客了,EC之前没有遗憾了

代码

#include <iostream>
#include <vector>
#include <cstring>
#include <deque>

using namespace std;
struct edge{
    long long from;
    long long to;
    long long flow;
    long long cap;
    long long cost;
    long long nxt;
    edge(long long f,long long t,long long ca,long long co,long long n):from(f),to(t),cap(ca),cost(co),nxt(n){
        flow=0;
    }
    edge(){}
};
long long egs[1005];
vector<edge> edges;
void addedge(long long f,long long t,long long ca,long long co){
    edges.emplace_back(f,t,ca,co,egs[f]);
    egs[f]=edges.size()-1;
    edges.emplace_back(t,f,0,-co,egs[t]);
    egs[t]=edges.size()-1;
}
long long dis[1005];
long long pre[1005];
long long flower[1005];
bool vis[1005];
long long total;
long long bellmanford(long long s,long long t)
{
    memset(dis,0x3f,sizeof dis);
    memset(vis,0,sizeof vis);
    memset(pre,-1,sizeof pre);
    memset(flower,0,sizeof flower);
    dis[s]=0;
    flower[s]=0x3f3f3f3f3f3f3f3f;
    deque<long long> qq;
    qq.push_back(s);
    vis[s]=true;
    while(!qq.empty())
    {
        long long now=qq.front();
        qq.pop_front();
        for(long long i=egs[now];i!=-1;i=edges[i].nxt)
        {
            if(edges[i].cap-edges[i].flow>0 && dis[edges[i].to]>dis[now]+edges[i].cost)
            {
                dis[edges[i].to]=dis[now]+edges[i].cost;
                pre[edges[i].to]=i;
                flower[edges[i].to]=min(flower[now],edges[i].cap-edges[i].flow);
                if(!vis[edges[i].to])
                {
                    if((!qq.empty()) && dis[edges[i].to]<dis[qq.front()])
                        qq.push_front(edges[i].to);
                    else
                        qq.push_back(edges[i].to);
                    vis[edges[i].to]=true;
                }
            }
        }
        vis[now]=false;
    }
    return flower[t];
}
long long mfmc(long long s,long long t,long long c){
    long long maxflow=0;
    long long mincost=0;
    long long nowflow=0;
    nowflow=bellmanford(s,t);
    while(nowflow>0){//直接把nowflow=bellmanford(s,t)放进来在oj上好像会死循环不知道为啥
        if(nowflow*dis[t]+mincost<c){
            maxflow+=nowflow;
            mincost+=dis[t]*nowflow;
            long long now=t;
            while(now!=s){
                edges[pre[now]].flow+=nowflow;
                edges[pre[now]^1].flow-=nowflow;
                now=edges[pre[now]].from;
            }
        } else{
            maxflow+=(c-mincost)/dis[t];//预算不足就平摊到整个路径上去使得最小值最大
            break;
        }
        nowflow=bellmanford(s,t);
    }
    return maxflow;
}
int main() {
    long long n, m, f;
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> f;
    memset(egs, -1, sizeof egs);
    edges.clear();
    total = n;
    for (long long i = 0; i < m; i++) {
        long long a, b, c, d;
        cin >> a >> b >> c >> d;
        addedge(a, b, c, d);
        addedge(b, a, c, d);//加了这句话就过了,死在无向图20发
    }
    cout << mfmc(1, n, f) << "\n";//除了加了预算限制外就是裸的费用流
    return 0;
}

感谢我校最强银牌图论选手女装凉

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值