link

最小割

双倍经验

这道题运用了最小割最常用的一种用法:集合划分

因为源汇最小割即就是将源汇划分到不同的集合,那么最简单的应用就是最小代价划分集合了。

本题中,题意是将 \(n\cdot m\) 个学生划分文理科,每人只能选一科且选不同的科有不同的收益,求最大收益,符合集合划分的条件,就理所当然地想到了最小割。

至于求最大收益,不妨就先将所有收益加起来,再减去最小代价(即最小割),便是最大收益了。

但是本题的难点在于,如果相邻同学选一样的(以下称为一个组合),还会有额外收益。

于是我们需要加一点限制,使得我们在最后求最小割的时候,对于每一个组合:要么满足组合内的所有成员,都在同一个子集(包含源点的子集 \(S\) 或包含汇点的子集 \(T\)),且那条代表额外收益的边不会被割掉;要么不满足组合内是所有成员,都在同一个子集,且那条代表额外收益的边被割掉了

于是大致见图思路出来了:

  • 对于每一个点(每一位同学)\(i\):

    连 \(s\rightarrow i\) 容量为 \(art_i\);

    连 \(i\rightarrow t\) 容量为 \(science_i\)。

  • 对于每一个组合 \(i\),新建两个点 \(x_i,y_i\):

    连 \(s\rightarrow x_i\) 容量为 \(same\_art_i\);

    连 \(y_i\rightarrow t\) 容量为 \(same\_science_i\)。

    对于该组合内的每个点(即该点+上下左右四个点)\(j\in i\):

    连 \(x_i\rightarrow j\) 容量为 \(+\infty\);

    连 \(j\rightarrow y_i\) 容量为 \(+\infty\)。

这里解释一下这么连的原因:

如果要 \(s\rightarrow x_i\) 这条边(即要这个组合所有同学都选文科的收益),那么就不割这条边。但是,又因为 \(x_i\) 向这个组合内每个点都连了一条 \(+\infty\) 的边,所以这些边便不会被割掉。那么为了防止 \(s\) 与 \(t\) 联通,自然就会割掉这个组合内每个点与 \(t\) 连的边(即都不选理科)。反之都选理科亦然。

如果放弃这个组合(即这个组合内每个成员选的科不都一样),那么就会割掉 \(s\rightarrow x_i\) 和 \(y_i\rightarrow t\) 这两条边,那么就相当于这个组合内的每个点都互相独立了,可以任意选科。

:不能将 \(x_i\) 与 \(y_i\) 合并成一个点来连边,这样会使上述放弃组合的情况无法达到(即无法破坏组合独立选择)。

Code

#include<bits/stdc++.h>
//#define int long long
#define pair pair<int,int>
using namespace std;
inline void end()
{
puts("");
system("pause");
}
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
const int N=3e4+4,M=5e5+5;
int n,m,nm,s,t,ans,Maxflow;
int first[N],nex[M],to[M],w[M],num=1;
inline void add(int u,int v,int val)
{
nex[++num]=first[u];
first[u]=num;
to[num]=v;
w[num]=val;
}
inline void Add(int u,int v,int val)
{
add(u,v,val);
add(v,u,0);
}
namespace ISAP
{
int dep[N],gap[N],cur[N];
void bfs()
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
queue<int> q;
q.push(t);
dep[t]=0;gap[0]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=first[u];i;i=nex[i])
{
int v=to[i];
if(dep[v]!=-1) continue;
dep[v]=dep[u]+1;
gap[dep[v]]++;
q.push(v);
}
}
}
inline int dfs(int u,int in)
{
if(u==t) return in;
int out=0;
for(int i=cur[u];i;i=nex[i])
{
cur[u]=i;
int v=to[i];
if(!w[i]||dep[v]!=dep[u]-1) continue;
int res=dfs(v,min(w[i],in-out));
w[i]-=res;
w[i^1]+=res;
out+=res;
if(in==out) return out;
}
gap[dep[u]]--;
if(!gap[dep[u]]) dep[s]=3*nm+3;
dep[u]++;
gap[dep[u]]++;
return out;
}
void work()
{
bfs();
while(dep[s]<3*nm+2)
{
memcpy(cur,first,sizeof(first));
Maxflow+=dfs(s,1e9);
}
}
}
inline int id(int i,int j){return (i-1)*m+j;}
int dx[5]={-1,0,1,0,0},dy[5]={0,-1,0,1,0};
int main()
{
//1|nm|nm|nm|1
//源点|每个组合"选文"|每个座位|每个组合"选理"|汇点
n=read(),m=read(),nm=n*m;
s=0,t=3*nm+1;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int val=read();ans+=val;
Add(s,id(i,j)+nm,val);//s -> i
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int val=read();ans+=val;
Add(id(i,j)+nm,t,val);//i -> t
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int val=read();ans+=val;
Add(s,id(i,j),val);//s -> x_i
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int val=read();ans+=val;
Add(id(i,j)+2*nm,t,val);//y_i -> t
for(int k=0;k<5;++k)
{
int x=i+dx[k],y=j+dy[k];
if(x<1||y<1||x>n||y>m) continue;
Add(id(i,j),id(x,y)+nm,1e9);//x_i -> j
Add(id(x,y)+nm,id(i,j)+2*nm,1e9);//j -> y_i
}
}
}
ISAP::work();
printf("%d",ans-Maxflow);
end();
return 0;
}

Luogu P4313 文理分科的更多相关文章

  1. BZOJ 3894 Luogu P4313 文理分科 (最小割)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=3894 (luogu) https://www.luogu.org/pro ...

  2. BZOJ 3894 / Luogu P4313 文理分科 (拆点最小割)

    题面 中文题面- BZOJ 传送门 Luogu 传送门 分析 这道题类似于BZOJ 3774 最优选择,然后这里有一篇博客写的很好- Today_Blue_Rainbow's Blog 应该看懂了吧- ...

  3. P4313 文理分科 最小割

    $ \color{#0066ff}{ 题目描述 }$ 文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过) 小P所在的班级要进行文理分科.他的班级可以用一个n*m的矩阵进行描述,每个格 ...

  4. P4313 文理分科

    思路 遇到这种利益冲突的最终利益最大化问题 考虑转化为最小割,使得损失的价值最小 相当于文科是S,理科是T,选出最小割就是确定损失代价最小的方案 然后就把S向每个点连一条cap=art[i][j]的边 ...

  5. BZOJ 3894: 文理分科 [最小割]

    3894: 文理分科 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 674  Solved: 392[Submit][Status][Discuss] ...

  6. Bzoj3894 文理分科

    Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 667  Solved: 389 Description  文理分科是一件很纠结的事情!(虽然看到这个题 ...

  7. bzoj 3894: 文理分科

    Description  文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠 结过)  小P所在的班级要进行文理分科.他的班级可以用一个n*m的矩阵进行 描述,每个格子代表一个同学的座位. ...

  8. BZOJ_3894_文理分科&&BZOJ_2127_happiness_最小割

    BZOJ_3894_文理分科_最小割 Description  文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠 结过)  小P所在的班级要进行文理分科.他的班级可以用一个n*m的矩阵进 ...

  9. BZOJ3894文理分科——最小割

    题目描述  文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠 结过)  小P所在的班级要进行文理分科.他的班级可以用一个n*m的矩阵进行 描述,每个格子代表一个同学的座位.每位同学必须从 ...

随机推荐

  1. Redis持久化锦囊在手,再也不会担心数据丢失了

    大家好,我是小羽. Redis 的读写都是在内存中进行的,所以它的性能高.而当我们的服务器断开或者重启的时候,数据就会消失,那么我们该怎么解决这个问题呢? 其实 Redis 已经为我们提供了一种持久化 ...

  2. AutomicBoolean

    AutomicBoolean 介绍 java并发包下提供的原子变量,是原子类其中之一.基本特性是在多线程环境下,多个线程同时执行这些类的实例包含的方法时,具有排他性 当某个线程进入方法,不会被其他线程 ...

  3. AI推理与Compiler

    AI推理与Compiler AI芯片编译器能加深对AI的理解, AI芯片编译器不光涉及编译器知识,还涉及AI芯片架构和并行计算如OpenCL/Cuda等.如果从深度学习平台获得IR输入,还需要了解深度 ...

  4. CodeGen字段循环Field Loop

    CodeGen字段循环Field Loop 字段循环是一个模板文件构造,它允许迭代CodeGen拥有的有关字段的集合.这些字段定义可以来自以下两个位置之一: •如果基于从存储库结构中获取的信息生成代码 ...

  5. 深入理解ES8的新特性SharedArrayBuffer

    简介 ES8引入了SharedArrayBuffer和Atomics,通过共享内存来提升workers之间或者worker和主线程之间的消息传递速度. 本文将会详细的讲解SharedArrayBuff ...

  6. MapReduce —— MapTask阶段源码分析(Output环节)

    Dream car 镇楼 ~ ! 接上一节Input环节,接下来分析 output环节.代码在runNewMapper()方法中: private <INKEY,INVALUE,OUTKEY,O ...

  7. 【工具解析】瑞士军刀bettercap2.X解析_第一期_编写HTTP代理注入模块_http(s).proxy.script

    /文章作者:Kali_MG1937 CNBLOG博客号:ALDYS4 QQ:3496925334/ 前言 bettercap已经从1.6更新至2.0版本 语言也从ruby改为了go 编写注入模块指定的 ...

  8. Java IO学习笔记八:Netty入门

    作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...

  9. 不会 Web 开发,也能让数据“动”起来的开源项目!

    本文面向有 Python 基础的小伙伴,有 Web 基础的更好 作者:HelloGitHub-吱吱 这里是 HelloGitHub 推出的<讲解开源项目>系列,今天要向小伙伴们介绍的是一个 ...

  10. idea自动更新代码

    如何开启或关闭idea的自动更新代码? File-Setting-Appearance&Beha-System Setting-Updates 选中或取消勾选Automatically che ...