CSDN同步

原题链接

前置知识:

从三种算法剖析网络流本质

简要题意:

给定网络图,求图的最大流,以及流量为最大流时的最小费用。

现在假设你们看了那篇网络流博客之后,所有人都会了 \(\text{EK , FF , dinic}\) 算法。

然后我们来介绍一个新的思想。

假设我们从最短路的角度出发,仍然采用之前那个 “反悔” 思想,那么此时 可以用 \(\text{SPFA}\) 来实现每次增广,增广的时候,你会发现原来代码里的 found 函数用来查询反向边。但是这次我们不用了,我们再建边 \(u \rightarrow v\) 的时候,就建立一个 \(\text{rev}\),表示当前边在另外一个顶点里的编号。

比方说 \(3 \rightarrow 5\) 在 \(3\) 的边中是第 \(2\) 条,那么 \(5\) 对于这条边的 \(\text{rev}=2\),便于我们迅速查找反向边。

然后考虑如何在最大流的基础上求出最小费用?我们沿袭 \(\text{SPFA}\) 中的 \(\text{dis}\),这是用来求最短路的,那么我们正好用 \(\text{dis}\) 把费用(即最短路中的边权)记录一下,正好可以达到这个效果。

所以说 \(\text{SPFA}\) 在某种意义上并没有死,因为它的常数出奇地小或许能卡过一些题目(比方说这道)。

当然了,如果真想用 \(\text{dijkstra}\) ,那么要用到 Johnson 全源最短路 中 \(\text{Johnson}\) 提出的,“构造上帝节点,依据最短路将边权构造为非负” 的思想。那样的话复杂度就是 \(O(nm \log n)\) 了。

时间复杂度:\(O(n^2m)\).(显然 \(\text{SPFA}\) 一次是 \(n^2\) 的)

实际得分:\(100pts\).(因为实际上根本跑不满的,不可能每次都会到上限)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std; const int N=5e4+1; inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;} struct node {
int to,flow,rev,cost; //to 是边终点 , flow 是流量 , rev 是反向边编号 , cost 是费用
} ;
vector<node> G[N];
int dis[N],pre1[N],pre2[N]; //pre1[i] 是正常网络流的前驱 pre2[i] 是前驱的边编号 , 类似于 rev , 有利于最后的反悔操作
int flow[N],n,m,s,t,ans;
bool vis[N]; int maxf; inline bool SPFA() {
fill(dis+1,dis+1+n,2e9);
memset(vis,0,sizeof(vis));
queue<int> q; dis[s]=0; vis[s]=1; flow[s]=2e9;
q.push(s);
while(!q.empty()) {
int u=q.front(); q.pop(); vis[u]=0;
// printf("%d\n",u);
for(int i=0;i<G[u].size();i++) {
node t=G[u][i]; int v=t.to; //向外增广
if(t.flow>0 && t.cost+dis[u]<dis[v]) {
dis[v]=dis[u]+t.cost;
pre1[v]=u; pre2[v]=i;
flow[v]=min(flow[u],t.flow); //记录答案
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
// for(int i=1;i<=n;i++) printf("%d ",dis[i]); puts("");
// for(int i=1;i<=n;i++) printf("%d ",flow[i]); puts("");
// for(int i=1;i<=n;i++) printf("%d ",pre1[i]); puts("");
// for(int i=1;i<=n;i++) printf("%d ",pre2[i]); puts("");
return dis[t]!=(2e9);
} inline void update() {
int k=t; while(k-s){ //不到源点就继续反悔
int p=pre1[k],q=pre2[k];
G[p][q].flow-=flow[t];
G[k][G[p][q].rev].flow+=flow[t]; //套路
k=p;
} maxf+=flow[t];
ans+=dis[t]*flow[t]; //费用更新
} inline void dinic() {
while(SPFA()) update();
} int main(){
n=read(),m=read(),s=read(),t=read();
while(m--) {
int u=read(),v=read(),w=read(),f=read();
G[u].push_back(node{v,w,G[v].size(),f});
G[v].push_back(node{u,0,G[u].size()-1,-f});
} dinic();
printf("%d %d\n",maxf,ans);
return 0;
}

P3381 【模板】最小费用最大流 题解的更多相关文章

  1. P3381 [模板] 最小费用最大流

    EK  + dijkstra (2246ms) 开氧气(586ms) dijkstra的势 可以处理负权 https://www.luogu.org/blog/28007/solution-p3381 ...

  2. 【洛谷 p3381】模板-最小费用最大流(图论)

    题目:给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 解法:在Dinic的基础下做spfa算法. 1 #include<cst ...

  3. 洛谷P3381 (最小费用最大流模板)

    记得把数组开大一点,不然就RE了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 ...

  4. 洛谷.3381.[模板]最小费用最大流(zkw)

    题目链接 Update:我好像刚知道多路增广就是zkw费用流.. //1314ms 2.66MB 本题优化明显 #include <queue> #include <cstdio&g ...

  5. UVA 1658 海军上将(拆点法+最小费用限制流)

    海军上将 紫书P375 这题我觉得有2个难点: 一是拆点,要有足够的想法才能把这题用网络流建模,并且知道如何拆点. 二是最小费用限制流,最小费用最大流我们都会,但如果限制流必须为一个值呢?比如这题限制 ...

  6. P3381 【模板】最小费用最大流(MCMF)

    P3381 [模板]最小费用最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入格式 第一行包含四个正整数N ...

  7. P3381 【模板】最小费用最大流

    P3381 [模板]最小费用最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入输出格式 输入格式: 第一行 ...

  8. 洛谷P3381 - 【模板】最小费用最大流

    原题链接 题意简述 模板题啦~ 题解 每次都以费用作为边权求一下最短路,然后沿着最短路增广. Code //[模板]最小费用最大流 #include <cstdio> #include & ...

  9. 洛谷P3381 最小费用最大流模板

    https://www.luogu.org/problem/P3381 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用 ...

随机推荐

  1. Java基础面试系列(一)

    Java基础面试总结(一) 1. 面向对象和面向过程的区别 面向过程 面向对象 性能 高于面向对象 类加载的时候需要实例化,比较消耗资源 三易(易维护,易复用,易扩展) 不如面向对象 具有封装,继承, ...

  2. vue的watch

    watch它可以用来监测Vue实例上的数据变动  尽量一张图解释清楚(尝试用圈圈区分关系): 写的很简单,watch本来就没啥东西我理解为响应式侦听全局变量 watch里绑定全局变量,被绑定全局变量发 ...

  3. LinkedHashMap源码解读

    1. 前言 还是从面试中来,到面试中去.面试官在面试 Redis 的时候经常会问到,Redis 的 LRU 是如何实现的?如果让你实现 LRU 算法,你会怎么实现呢?除了用现有的结构 LinkedHa ...

  4. 03-influxdb原理

    influxdb基本操作 1. influxdb与传统数据库区别 influxdb 传统数据库 database 数据库 measurement 表 points 表里的一行数据 2. 基本原理 2. ...

  5. JVM笔记-运行时内存区域划分

    1. 概述 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分为若干个不同的数据区域.它们各有用途,有些随着虚拟机进程的启动一直存在(堆.方法区),有些则随着用户线程的启动和结束而建立 ...

  6. iframe 父框架调用子框架的函数

    1.父框架定义: <iframe name="mainframe" id="mainframe" width="100%" scrol ...

  7. 谈谈一些逻辑相同,性能差异却很大的sql

    总结写在前面: 1. 本篇讲述了三个例子,其本质都是揭示了若对索引字段做函数操作,可能会破坏索引值的有序性,由此优化器就决定放弃走树搜索功能. 2. 由第1点提供了一个优化思路,即我们能否避免或转化s ...

  8. github的上面的安全工具

    简介 Scanners Box也被称为 scanbox,是一个强大完备的黑客工具箱,它收集了Github上数10种类别的开源扫描器,包括子域名,数据库,中间件和其他模块化设计的扫描器等,但对于一些被大 ...

  9. 部署harbor以https模式和k8s对接

    集群时间同步 我们在之前的kubeasz部署高可用kubernetes1.17.2 并实现traefik2.1.2部署篇已经实现了基于chrony的时间同步 [root@bs-k8s-master01 ...

  10. ThinkPHP的几种路由形式总结

    本篇文章总结了ThinkPHP的几种路由形式,分别是普通模式.PATHINFO.REWRITE和兼容模式,希望对各位学习thinkphp的朋友有帮助! thinkphp官方文档里有详细的说明: Thi ...