前置知识

平面图

平面图就是平面上任意边都不相交的图。(自己瞎画的不算XD)

对偶图

比如说这个图,我们发现平面图肯定会把平面分成不同的区域(感觉像拓扑图),并把这些区域当做每个点(不被包围的区域独自成点,如本图4*),给相邻的区域连上边,就转化成了一个对偶图(图中红色)

网络流的图中有两个点:原点和汇点。割就是删去的一些边使原点和汇点无法连接(不太严谨)

看题!bzoj1001

既然有了原点和汇点,那么就不能简单的把外部看做一个点了,我们把外部分成两个点——超级原点和超级汇点!

然后像上面一样建边,求对偶图的最短路就行了!!!

你问我如何判断对偶图的点之间割了哪些边?

emmm这就是它恶心的地方了——建图并不容易。不过,它给边的方式还是有点人性的。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<utility>
#include<queue>
#include<functional>
#include<vector>
using namespace std;
inline int read()
{
int x=0,w=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c) )x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
const int maxn=3000000;
typedef pair<int,int> pii;
int n,m,node[1210][1210][2];
int ecnt,t[maxn<<1],nxt[maxn<<1],head[maxn<<1],val[maxn<<1];
inline void addedge(int from,int to,int dis)
{
t[++ecnt]=to;nxt[ecnt]=head[from];head[from]=ecnt;val[ecnt]=dis;
t[++ecnt]=from;nxt[ecnt]=head[to];head[to]=ecnt;val[ecnt]=dis;
}
int dis[maxn];
bool vis[maxn];
inline void dijkstra(int start,int end)
{ memset(dis,0x3f3f3f3f,sizeof dis);
priority_queue<pii,vector<pii >,greater<pii > > q;
q.push(make_pair(0,start)),dis[start]=0;
while(!q.empty())
{
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=t[i],w=val[i];
if(dis[v]>=dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(make_pair(dis[v],v));
}
}
}
}
int main()
{
int v=1;
n=read()-1,m=read()-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<2;k++)
node[i][j][k]=v++;
int start=v++,end=v;
for(int j=1;j<=m;j++)addedge(start,node[1][j][0],read());
for(int i=1;i<=n-1;i++)
for(int j=1;j<=m;j++)
addedge(node[i][j][1],node[i+1][j][0],read());
for(int j=1;j<=m;j++)addedge(end,node[n][j][1],read());
/*横行*/
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1;j++)
if(j==1)addedge(end,node[i][j][1],read());
else if(j==m+1)addedge(start,node[i][j-1][0],read());
else addedge(node[i][j-1][0],node[i][j][1],read());
/*纵行*/
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
addedge(node[i][j][0],node[i][j][1],read());
/*斜行*/
dijkstra(start,end);
printf("%d\n",dis[end]);
return 0;
}

好了,以上只是针对bzoj1001的问题的解法。实际上,这个问题还有一些通用的解法(只不过出题人不想让大家用加强了数据)

不过上一道题我们也可以用下面的方式求出网络的最小割。

不过我们换一题XD

P2598狼和羊的故事

(反正狼就是nb)

通过读题,我们发现,这个orez想圈养狼真是了不起(姜戎都不敢这么干)

最大流最小割定理:网络的最大流等于最小割

证明也比较简单(但我不会严谨的),感性理解一下,最大流一定有一些边是满的,我们把这些边割了它就流不成了。对于其他的边,要么不是必经之路,要么边权不比同一条流上的最大流的边小,所以~~得证~~

那么这道题的话其他前辈已经讲得很好了,即求法就是

1. 将所有狼连到原点,边权INF
2. 将所有羊连到汇点,边权INF
3. 将所有点的四周加边,边权为1

这是一个对偶图的思想,相当于组成了一个网络,在这个网络中,只要点与点之间有边相连就相当于之间没有栅栏,所以一开始是全部连接的。我们要做的,就是砌栅栏把一些边断掉,使狼和羊分离。因为所有狼和所有羊都连在原点和汇点,这就相当于求最小割了。
(不知道讲清楚没有)

前面两个大家应该都清楚,边权INF相当于没有影响只是把所有狼/羊连在一起罢了。第三步就是连边:因为修一个栅栏需要1,所以边权为1,简直和对偶图一模一样(本来就是一个思想)
同样的,这里的难度就在于建边,建完求最大流就行了。

注意数组的大小。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<utility>
#include<queue>
using namespace std;
inline int read()
{
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=100005,INF=0x3f3f3f3f;
int n,m;
int mapp[105][105];
int ecnt=1,head[maxn],t[maxn<<1],nxt[maxn<<1],val[maxn<<1];
inline void addedge(int from,int to, int dis)
{
t[++ecnt]=to;val[ecnt]=dis;nxt[ecnt]=head[from];head[from]=ecnt;
t[++ecnt]=from;val[ecnt]=0;nxt[ecnt]=head[to];head[to]=ecnt;
}
int fx[]={0,1,0,-1},fy[]={1,0,-1,0};
int cnt;
int dep[maxn],start,end,cur[maxn];
inline bool BFS()
{
queue<int> q;
for(int i=1;i<=cnt;i++)dep[i]=-1,cur[i]=head[i];
dep[start]=0;
q.push(start);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=nxt[i])
if(val[i] and dep[t[i]]==-1)
dep[t[i]]=dep[u]+1,q.push(t[i]);
}
if(dep[end]==-1)return 0;
return 1;
} int DFS(int x,int flow)
{
if(x==end)return flow;
int used=0;
for(int i=cur[x];i;i=nxt[i])
{
cur[x]=i;
int u=t[i];
if(val[i] and dep[u]==dep[x]+1)
{
int w=DFS(u,min(val[i],flow-used));
used+=w;
val[i]-=w;
val[i^1]+=w;
if(used==flow)return flow;
}
}
if(!used)dep[x]=-1;
return used;
}
inline void build()
{
n=read(),m=read();
cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mapp[i][j]=++cnt;
start=++cnt,end=++cnt;
for(int zp,i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if((zp=read())==1)addedge(start,mapp[i][j],INF);
else if(zp==2)addedge(mapp[i][j],end,INF);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<4;k++)
{
int xx=i+fx[k],yy=j+fy[k];
if(xx<1 or xx>n or yy<1 or yy>m)continue;
addedge(mapp[i][j],mapp[xx][yy],1);
}
}
inline void work()
{
build();
int ans=0;
while(BFS())ans+=DFS(start,INF);
printf("%d\n",ans);
}
}
int main()
{
star::work();
return 0;
}

最大流最小割——bzoj1001狼抓兔子,洛谷P2598的更多相关文章

  1. bzoj1001狼抓兔子 对偶图优化

    bzoj1001狼抓兔子 对偶图优化 链接 https://www.lydsy.com/JudgeOnline/problem.php?id=1001 思路 菜鸡总是要填坑的! 很明显让你求网格图的最 ...

  2. 【bzoj1001】【最短路】【对偶图】【最大流转最小割】狼抓兔子题解

    [BZOJ1001]狼抓兔子 1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 18872  Solved ...

  3. BZOJ-1001 狼抓兔子 (最小割-最大流)平面图转对偶图+SPFA

    1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec Memory Limit: 162 MB Submit: 14686 Solved: 3513 [Submit][ ...

  4. BZOJ1001:狼抓兔子(最小割最大流+vector模板)

    1001: [BeiJing2006]狼抓兔子 Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨, ...

  5. bzoj1001狼抓兔子

    1001: [BeiJing2006]狼抓兔子 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你 ...

  6. BZOJ1001 狼抓兔子 平面图转对偶图 最小割

    现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: 左上角点为 ...

  7. BZOJ1001 狼抓兔子(裸网络流)

    Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一 ...

  8. 【建图+最短路】Bzoj1001 狼抓兔子

    Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个 ...

  9. [日常摸鱼]bzoj1001狼抓兔子-最大流最小割

    题意就是求最小割- 然后我们有这么一个定理(最大流-最小割定理 ): 任何一个网络图的最小割中边的容量之和等于图的最大流. (下面直接简称为最大流和最小割) 证明: 如果最大流>最小割,那把这些 ...

随机推荐

  1. nacos 实战(史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  2. VisualStudio自动添加注释

    1.VisualStudio  新开页自动添加注释 a 在团队开发或者公司开发中,我们一般都喜欢给自己所创建的类或者接口以及其它模板设置版权说明,但是每个类一个一个的去加又是非常的费劲,所以一般情况下 ...

  3. STM32学习笔记-NVIC中断知识点

    STM32学习笔记-NVIC中断知识点总结 中断优先级设置步骤 1. 系统运行后先设置中断优先级分组 函数:void NVIC_PriorityGroupConfig(uint32_tNVIC_Pri ...

  4. 办公利器!用Python批量识别发票并录入到Excel表格

    辰哥今天来分享一篇办公干货文章:用Python批量识别发票并录入到Excel表格.对于财务专业等学生或者公司财务人员来说,将报账发票等汇总到excel简直就是一个折磨. 尤其是到年底的时候,公司的财务 ...

  5. 基于ABP落地领域驱动设计-05.实体创建和更新最佳实践

    目录 系列文章 数据传输对象 输入DTO最佳实践 不要在输入DTO中定义不使用的属性 不要重用输入DTO 输入DTO中验证逻辑 输出DTO最佳实践 对象映射 学习帮助 系列文章 基于ABP落地领域驱动 ...

  6. ZooKeeper分布式锁的实现

    ZooKeeper分布式锁的实现. 在分布式的情况下,sychornized 和 Lock 已经不能满足我们的要求了,那么就需要使用第三方的锁了,这里我们就使用 ZooKeeper 来实现一个分布式锁 ...

  7. vue项目打包成html,在本地点击直接能打开

    默认情况下vue项目打包后,本地打开index.html是空白的,有报错.Failed to load resource: net::ERR_FILE_NOT_FOUND 这时需要修改config-& ...

  8. sql循环说明

    while循环:主要是判断,不能使用表中的ID,临时表是ID自增的,通过自增ID可以查出表ID(语法简单,需要配合其他代码操作表ID)游标循环:可以使用表中的ID ,进行修改等操作(语法难一点,核心代 ...

  9. elementui——表格的相同内容单元格合并

    在今天工作中遇到了相同单元格需要合并的一个需求,实现记录如下. 实现效果: 任务要求: 对表中体系这一列相同的体系进行合并. 思路:定义一个空数组:[]定义一个变量:0遍历数据如果有相同数据 在空数组 ...

  10. Linux 动态库 undefined symbol 原因定位与解决方法

    在使用动态库开发部署时,遇到最多的问题可能就是 undefined symbol 了,导致这个出现这个问题的原因有多种多样,快速找到原因,采用对应的方法解决是本文写作的目的. 可能的原因 依赖库未找到 ...