前言

你说你是个网络流的题,就算了嘛,还要输出方案,啧啧啧...

题目

题目描述

你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶。很不幸,你发现这件事的时候,有三聚氰胺的牛奶已经进入了送货网。这个送货网很大,而且关系复杂。你知道这批牛奶要发给哪个零售商,但是要把这批牛奶送到他手中有许多种途径。送货网由一些仓库和运输卡车组成,每辆卡车都在各自固定的两个仓库之间单向运输牛奶。在追查这些有三聚氰胺的牛奶的时候,有必要保证它不被送到零售商手里,所以必须使某些运输卡车停止运输,但是停止每辆卡车都会有一定的经济损失。你的任务是,在保证坏牛奶不送到零售商的前提下,制定出停止卡车运输的方案,使损失最小。

输入格式

第一行: 两个整数N(2<=N<=32)、M(0<=M<=1000), N表示仓库的数目,M表示运输卡车的数量。仓库1代 表发货工厂,仓库N代表有三聚氰胺的牛奶要发往的零售商。 第2..M+1行: 每行3个整数Si,Ei,Ci。其中Si,Ei表示这 辆卡车的出发仓库,目的仓库。Ci(0 <= C i <= 2,000,000) 表示让这辆卡车停止运输的损失。

输出格式

两个整数C、T:C表示最小的损失,T表示在损失最小的前提下,最少要停止的卡车数。

输入输出样例

输入

4 51 3 1003 2 502 4 601 2 402 3 80

输出

60 1

说明/提示

题目翻译来自NOCOW。

USACO Training Section 4.4

其他

USACO网站上还要求输出方案,即停止了哪些卡车,字典序输出

(哇,好恶心qwq!)

分析

哎,这道题+博客工程量巨大,本来想咕掉来着...

既然下定决心要写,那我们就开始吧...


【洛谷版】洛谷的这道题简化了,没有让你输出方案,于是会好处理很多

1.本题第一问求“使起点与终点变成两个独立的连通块的最小费用”,最小割板题,可以直接套用【最小割】即【最大流】的板子,我用的Dinic

2.关于求“割边的数量”,有个十分巧妙的方法 / 重点!敲黑板!:

【法一】

建两次图,一次按原边权建图跑最大流求得最小割,再按边权为1建图跑最大流求割的边数

【法二】更优秀的做法qwq

建图时将边权w=w*a+1(w为本来的边权,a为大于1000的数),这样我们能求得最大流ans,

则最小割的值为ans/a,割的边数为ans%a

这很容易理解,但是还是解释一下:

因为最小割的边集中有w1+w2+w3…+wn=ans(这个ans为本来的最小割),

所以必然有w1*a+w2*a+w3*a…+wn*a=ans*a,

于是必然有w1*a+1+w2*a+1+w3*a+1…+wn*a+1=ans*a+k(k为最小割的边数,k<=m<=1000),

这样就很明显了,因为边数m不大于1000,所以k的最大值为1000,

我们只要使设定的a的值大于1000,那么按上述方法建图,跑出的最大流除以a就是最小割的值ans,最大流模a就是最小割的边数k


【USACO版】要求按字典序输出割边的编号

参考某大佬博客:https://blog.csdn.net/csyzcyj/article/details/11951595

本题思路为网络流求最大流和最小割边集。

首先加流量。为了构造解,我记录了输入的顺序并对边的大小进行了排序

<排序按边权从大到小排序,这一步主要是为了后面求最小割>,

然后先求一遍最大流S,然后枚举去掉每一条边<设边权值为T>,

然后求当前最大流X,若S=T+X,则该边在最小割边集中,将该边从图中去掉,S=X。

注意,每次DINIC后若用同一个数组记流量,需在DINIC后还原。

但是为什么【排序按边权从大到小排序】呢?这里我不是很懂

这里有个例子,但是本蒟蒻解释不清楚为什么qwq...

8 9
1 2 2
1 3 1
3 4 1
2 4 2
4 5 3
5 6 1
5 7 2
7 8 2
6 8 1

如果不排序,会输出2,要割1、2这两条边,但是正确答案是割5这条边

后来看了另某大佬的博客才恍然大悟——

因为要找最少的边(有的oj还要找出边的标号),所以要把边从大到小排一次序,就能更好的找出“最少的边”


【某大佬的讲解完整版】我觉得讲得也很好,贴一下 :https://www.luogu.org/blog/kkksc03666/solution-p1344

这一道题是由最小割转最大流

我就这样解释吧:

最大流是从点1能流到n的最大流量,流量的大小主要是由每条路的最小边决定的(大概是这样的)

最小割为了消耗费用最小,就肯定要割去最小消耗的边。

也可以这样说,先找出1到n的最大流,把这些流量全部切掉,就是最小割(很多条边都是多余的),感觉和网络流一模一样

关于第二问:

因为要找最少的边(有的oj还要找出边的标号),所以要把边从大到小排一次序,就能更好的找出“最少的边”,为了不出

现低级错误,我就用了一个用时多一些但是不容易出错的方法来做

如果要找出这些边,就可以这样想一想,有一些边是“重要的”,就是说这一条边是满流量的,而且这种边是直接关系到

这条路(不是边)到终点的流量(就是刚刚说的最小边,流量是由最小边决定的)

其实就是把这一条边去掉后的最大流+这条边的流量=最大流,只要是找出这样的边就可以了

注意:这里有一个细节,就是可能会出现多条这样的边,这些边之和大于最大流,还有就是同一条路可能存在两条同流

量的边,这样的边就只要找一条


【其他要点】可能这个知识点很基础,但是我基础不好...qwq...现在才知道...

要用"^1"即异或1等的操作话(例如跑网络流会写到E[i^1]、表示前后对应关系等),

有时候必须从二开始,在此题中也就是int cnt=1,因为E[ ++cnt ]...所以边的计数从2开始(我寻思从0开始好像也可以)
e.g.
亦或:两个相等就为1,否则为0
0^1=0
1^1=1
2^1=10^01=11=3
3^1=11^01=10=2
4^1=100^001=101=5
5^1=101^001=100=4

AC代码——无输出方案

/*
ID:lunasmi2
TASK:ditch
LANG:C++
*/
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=2000,MAXM=2*MAXN,INF=0x3f3f3f3f,del=1005;
int head[MAXN+5],nxt[MAXM+5],cnt=1;
int dep[MAXN+5];
int n,m;
ll maxf;
struct Edge
{int u,v,f;Edge(int _u=0,int _v=0,int _f=0){u=_u,v=_v,f=_f;}Edge(Edge &e){u=e.u,v=e.v,f=e.f;}
}E[MAXM+5];
void Addedge(int u,int v,int f)
{E[++cnt]=Edge(u,v,f);nxt[cnt]=head[u];head[u]=cnt;
}
bool bfs()
{memset(dep,0,sizeof(dep));dep[1]=1;queue<int> que;que.push(1);while(!que.empty()){int u=que.front();que.pop();for(int i=head[u];i;i=nxt[i])if(E[i].f&&!dep[E[i].v]){dep[E[i].v]=dep[u]+1;que.push(E[i].v);}}if(!dep[n])return 0;return 1;
}
int dfs(int u,int f)
{if(u==n)return f;int w,used=0;for(int i=head[u];i;i=nxt[i])if(E[i].f&&dep[E[i].v]==dep[u]+1){w=dfs(E[i].v,min(f-used,E[i].f));used+=w;E[i].f-=w;E[i^1].f+=w;if(used==f)return f;}return used;
}
void dinic()
{while(bfs())maxf+=dfs(1,INF);
}
int main()
{//freopen("ditch.in","r",stdin);//freopen("ditch.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v,f;scanf("%d%d%d",&u,&v,&f);Addedge(u,v,f*del+1);Addedge(v,u,0);}dinic();printf("%lld %lld\n",maxf/del,maxf%del); return 0;
}

AC代码——有输出方案

/*
ID:lunasmi2
TASK:milk6
LANG:C++
*/
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100,MAXM=3000,INF=0x3f3f3f3f;
int head[MAXN+5],nxt[MAXM+5],cnt=1;//cnt必须从1开始!我也不知道为什么,从0开始时调了很久
int dep[MAXN+5],w[MAXM+5],ans[MAXM+5];
bool vis[MAXM+5];
int n,m,maxf,cnt_w,cnt_ans;
bool cmp(int a,int b)
{return a>b;
}
struct Edge
{int u,v,f,g;//g用来储存边权原值,全程不发生改变 Edge(int _u=0,int _v=0,int _f=0,int _g=0){u=_u,v=_v,f=_f,g=_g;}Edge(Edge &e){u=e.u,v=e.v,f=e.f,g=e.g;}
}E[MAXM+5];
void Addedge(int u,int v,int f,int g)
{E[++cnt]=Edge(u,v,f,g);nxt[cnt]=head[u];head[u]=cnt;
}
void restore()//边权恢复为原值
{for(int i=1;i<=2*m;i++)E[i].f=E[i].g;
}
bool bfs()
{memset(dep,0,sizeof(dep));dep[1]=1;queue<int> que;que.push(1);while(!que.empty()){int u=que.front();que.pop();for(int i=head[u];i;i=nxt[i])if(E[i].f&&!dep[E[i].v])//是正向边且没被访问过 {dep[E[i].v]=dep[u]+1;que.push(E[i].v);}}if(!dep[n])return 0;return 1;
}
int dfs(int u,int f)
{if(u==n)return f;int w,used=0;for(int i=head[u];i;i=nxt[i])if(E[i].f&&dep[E[i].v]==dep[u]+1){w=dfs(E[i].v,min(f-used,E[i].f));used+=w;E[i].f-=w;E[i^1].f+=w;if(used==f)return f;}return used;
}
void dinic()
{maxf=0;while(bfs())maxf+=dfs(1,INF);restore();//因为要多次跑最大流,所以跑完一遍就恢复成原图(除删掉的割边值一直为0)
}
int main()
{//freopen("milk6.in","r",stdin);//freopen("milk6.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v,f;scanf("%d%d%d",&u,&v,&f);w[++cnt_w]=f;Addedge(u,v,f,f);Addedge(v,u,0,0);}sort(w+1,w+m+1,cmp);//边必须按边权从大到小枚举 dinic();restore();//因为已经跑了一遍dinic,边权有变化,所以要恢复原值 printf("%d ",maxf);int Maxf=maxf;for(int i=1;i<=m;i++){for(int j=2;j<=2*m;j+=2)//按边权从大到小枚举被删去检查是否是割边的边 {if(!vis[j]&&E[j].f==w[i])//找到枚举到的边 {vis[j]=1;int tmp=E[j].f;E[j].f=0;//假若删去这条边 dinic();if(Maxf-tmp==maxf)//新最大流+该边权=原始最大流则该边为割边 {ans[++cnt_ans]=(j+1)/2;//因为加了反向边,所以边的编号有变化 E[j].g=E[j].f=0;//删掉该割边 Maxf=maxf;//最大流替换为新的最大流,继续找割边 }break;}}            }sort(ans+1,ans+cnt_ans+1);printf("%d\n",cnt_ans);for(int i=1;i<=cnt_ans;i++)//USA上要求输出删去的割边编号 printf("%d\n",ans[i]);return 0;
}

【网络流-最小割】USA4.4——追查坏牛奶Pollutant Control相关推荐

  1. P1344 [USACO4.4] 追查坏牛奶 Pollutant Control (网络流)

    P1344 [USACO4.4] 追查坏牛奶 Pollutant Control (网络流) 题目链接 文章目录 P1344 [USACO4.4] 追查坏牛奶 Pollutant Control (网 ...

  2. 洛谷 P1344 [USACO4.4] 追查坏牛奶Pollutant Control【网络流】

    省选Day2来临前给自己一个提醒:关键时刻记得开long long! 好,我们来看一个简单的网络流题. 如何在求最小割的同时得到最小割的边数呢? P1344 [USACO4.4]追查坏牛奶Pollut ...

  3. 洛谷 P1344 [USACO4.4]追查坏牛奶Pollutant Control 解题报告

    P1344 [USACO4.4]追查坏牛奶Pollutant Control 题目描述 你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶.很不幸,你发现这件事的时候 ...

  4. 洛谷P1344 [USACO4.4]追查坏牛奶Pollutant Control(网络流, 最大流最小割)

    初学网络流:http://blog.csdn.net/wzw1376124061/article/details/55001639 最大流最小割:http://blog.csdn.net/wzw137 ...

  5. [USACO Section 4.4]追查坏牛奶Pollutant Control (最小割)

    题目链接 Solution 一眼看过去就是最小割,但是要求割边最少的最小的割. 所以要用骚操作... 建边的时候每条边权 \(w = w * (E+1) + 1;\) 那么这样建图跑出来的 \(max ...

  6. USACO4.4 追查坏牛奶Pollutant Control

    题目链接:https://www.luogu.com.cn/problem/P1344 这显然是一道网络流题目 第一问裸的最大流 关键在于第二问怎么求 一个很妙的做法是将边权乘上大常数再加1,这样分成 ...

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

    类型:最小割 建模分析: 很明显,每个仓库就是节点,而车即为管道. 要求我们不能有从1->n的路径,就是把1,n分到两个集合去. 第一问我们把容量设为运输费用 套用最小割==最大流定理求出答案. ...

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

    https://www.luogu.org/problemnew/show/P1344 这道题很容易就可以看出是最小割=最大流. 但是要求出要割几条边就有些毒瘤了. ↓为废话 但orzn*inf后,蒟 ...

  9. P1344-[USACO4.4]追查坏牛奶Pollutant Control【网络流,最小割】

    正题 题目链接:https://www.luogu.org/problemnew/show/P1344 题目大意 要求1不能到n点需要去掉的边的权值之和最小,在这样的情况下求最少去掉的边. 解题思路 ...

最新文章

  1. 研究所月入两万,是一种什么体验?
  2. 银联的bankall_阿尔法银行罗马尼亚分行开始受理银联卡
  3. 谷歌发布 Linux 内核提权漏洞奖励计划,综合奖金最高超30万美元
  4. Linux基础系列4(ls,cp命令详解)
  5. Redis 6.0 源码阅读笔记(13 ) -- Redis 集群节点选举流程
  6. bzoj 1610 连线游戏
  7. 配置企业管理系统,什么样的工作流才有用
  8. adb连接雷电模拟器修改hosts
  9. 【笔记】Ring-DVFS:基于可靠性感知强化学习的DVFS,适用于实时嵌入式系统
  10. Fedora 32 Server 在ThinkPad X61上安装,自带了有线、无线网卡驱动
  11. unity python热更新_Unity热更新介绍和测试方法
  12. 这些“黑话”只有PCB设计制造内行人才懂!附PCB术语及英文对照
  13. 什么是面向切面编程?
  14. 解决k8s中的长连接负载均衡问题
  15. 攻防对抗形势下代码重用技术的演进
  16. Ajax同步获取数据
  17. 接单日记(三)文本处理之词云生成
  18. 什么网站可以测试敏感词?
  19. win10下的Cmd命令的初步认识
  20. 陕西省职业计算机考试试题,2013陕西省计算机等级考试试题 二级C试题试题及答案...

热门文章

  1. 我知道java的基本操作 日语翻译_日语文档怎么翻译?我来教会你日语翻译
  2. 路漫漫:网络空间的监管趋势
  3. MySQL 如何存储大数据
  4. 聊聊Mybatis里面的缓存机制吧
  5. MyBatis学习(9)—— MyBatis缓存
  6. win10计算机优化技巧,史上最全win10优化技巧 让你的win10系统从此不卡顿
  7. 医学图像分割之MedNeXt
  8. python 计算两个年份之间闰年的数量 判断某年份是否为闰年
  9. 正则表达式(在String中和使用Pattern类)
  10. Caffe 学习:Crop 层