Sol.

说实话,对于一个初学者,这道题很难看出是一道网络流-最小割。对于一个熟练者,这是比较套路的一种模型。

最小割,可以看做是在一个图中删掉最小的边权和使得源点、汇点不连通。或者换一个角度,可以看做是将图中的所有点以最小的代价分成两个阵营。

现在就有点像这道题了。我们以损失最小代价将这些学生分开为文理两阵营。

答案即为 \(\sum \limits _{i, j} \mathrm{Art}(i, j) + \sum \limits _{i, j} \mathrm{Science}(i, j) + \sum \limits _{i, j} \mathrm{SameArt}(i, j) + \sum \limits _{i, j} \mathrm{SameScience}(i, j) - d\)。

其中 \(d\) 就是我们要考虑的最小代价,可以根据上面的思路设想到它应该是一个流图的最小割。

研究以下这个图的一些限定条件,不妨设 \(S\) 为文阵营,即源点,\(T\) 为理阵营,即汇点。则有:

  • 对于一个割,所有的图中表示人的结点应该与且仅与 \(S,T\) 中的一个连通。
  • 当表示两个相邻的人的结点属于同一阵营时,一定有与它们属于另一阵营产生的额外价值等量的边(允许多条)属于割。
  • 再发现,若一条边的边权为极值,则可以看做我们强制绑定了两点,且该边一定不出现在割中。

我很难描述具体构图时的思路。

大概是灵活运用极值边权,以及多考虑若一边属于割则会损失多少权值这样的小事件。

基础想法先是将 \(S\) 和一个人相连,边权为 \(\mathrm{Art}\),再将该人与 \(T\) 相连,边权为 \(\mathrm{Science}\)。

对于一个人即它相邻的人,我们考虑加入两个虚点,到 \(S, T\) 分别连 \(\mathrm{SameArt}, \mathrm{SameScience}\),再将这两个点和这五个人(当前人和相邻人)绑定。

也就是说如果这五人当中有一人与 \(S\) 连通,则连向 \(T\) 的虚点一定会断开。

如下图。(图源网络,侵删。

那么接下来就是一个最小割了。


Code.

#include <queue>
#include <cstdio>
using namespace std; int Abs(int x) { return x < 0 ? -x : x; }
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; } int read() {
int x = 0, k = 1;
char s = getchar();
while(s < '0' || s > '9') {
if(s == '-')
k = -1;
s = getchar();
}
while('0' <= s && s <= '9') {
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * k;
} void write(int x) {
if(x < 0) {
x = -x;
putchar('-');
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
} void print(int x, char s) {
write(x);
putchar(s);
} const int MAXN = 1e2 + 5;
const int MAXM = 1e5 + 4e4 + 5;
const int MAXL = 3e4 + 5;
const int INF = 2147483647; struct Maximum_flow {
struct edge {
int v, nxt;
edge() {}
edge(int V, int Nxt) {
v = V, nxt = Nxt;
}
} e[MAXM << 1];
int n, cnt, s, t;
int Cap[MAXM << 1], Flow[MAXM << 1];
int Lab[MAXL], Cur[MAXL], head[MAXL];
queue<int> q; void init(int N, int S, int T) {
for(int i = 0; i <= cnt; i++)
Flow[i] = 0, Cap[i] = 0;
n = N, cnt = 0, s = S, t = T;
for(int i = 1; i <= n; i++)
head[i] = -1;
} void Add_Edge(int u, int v, int w) {
Cap[cnt] += w;
e[cnt] = edge(v, head[u]);
head[u] = cnt++;
e[cnt] = edge(u, head[v]);
head[v] = cnt++;
} bool Lab_Vertex() {
for(int i = 1; i <= n; i++)
Lab[i] = 0;
Lab[t] = 1;
while(!q.empty())
q.pop();
q.push(t);
while(!q.empty()) {
int v = q.front();
q.pop();
for(int i = head[v], u; ~i; i = e[i].nxt) {
u = e[i].v;
if(!Lab[u] && Cap[i ^ 1] - Flow[i ^ 1]) {
Lab[u] = Lab[v] + 1;
q.push(u);
if(u == s)
return Lab[s];
}
}
}
return Lab[s];
} int Widen(int u, int Limit) {
if(u == t)
return Limit;
int Used = 0, Delta;
for(int i = Cur[u], v; ~i; i = e[i].nxt) {
v = e[i].v;
Cur[u] = i;
if(Lab[v] + 1 != Lab[u] || Cap[i] - Flow[i] <= 0)
continue;
Delta = Widen(v, Min(Limit - Used, Cap[i] - Flow[i]));
Used += Delta, Flow[i] += Delta, Flow[i ^ 1] -= Delta;
if(Used == Limit)
return Used;
}
return Used;
} int Dinic() {
int res = 0;
while(Lab_Vertex()) {
for(int i = 1; i <= n; i++)
Cur[i] = head[i];
res += Widen(s, INF);
if(res < 0)
return INF;
}
return res;
}
} Flow_Graph; int dir[5][2] = {{0, 0}, {0, 1}, {1, 0}, {-1, 0}, {0, -1}};
int a[MAXN][MAXN][2], s[MAXN][MAXN][2], n, m; int Get(int x, int y) { return (x - 1) * m + y; } int main() {
n = read(), m = read();
int Sum = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
a[i][j][0] = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
s[i][j][0] = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
a[i][j][1] = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
s[i][j][1] = read();
int S = n * m * 3 + 1, T = n * m * 3 + 2;
Flow_Graph.init(T, S, T);
for(int i = 1; i <= n; i++)
for(int j = 1, Pos; j <= m; j++) {
Pos = Get(i, j);
Sum += a[i][j][0], Sum += a[i][j][1], Sum += s[i][j][0], Sum += s[i][j][1];
Flow_Graph.Add_Edge(S, Pos, a[i][j][0]);
Flow_Graph.Add_Edge(Pos, T, s[i][j][0]);
Flow_Graph.Add_Edge(S, Pos + n * m, a[i][j][1]);
Flow_Graph.Add_Edge(Pos + n * m * 2, T, s[i][j][1]);
for(int k = 0, x, y; k < 5; k++) {
x = i + dir[k][0], y = j + dir[k][1];
if(x > n || x < 1 || y > m | y < 1)
continue;
Flow_Graph.Add_Edge(Pos + n * m, Get(x, y), INF);
Flow_Graph.Add_Edge(Get(x, y), Pos + n * m * 2, INF);
}
}
print(Sum - Flow_Graph.Dinic(), '\n');
return 0;
}

Solution -「BZOJ3894」文理分科的更多相关文章

  1. 【BZOJ3894】文理分科(最小割)

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

  2. 【BZOJ3894】文理分科 最小割

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

  3. 【bzoj3894】文理分科 网路流

    [bzoj3894]文理分科 2015年3月25日3,4002 Description  文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠 结过)  小P所在的班级要进行文理分科.他的班 ...

  4. 【BZOJ3894】 文理分科

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

  5. Solution -「构造」专练

    记录全思路过程和正解分析.全思路过程很 navie,不过很下饭不是嘛.会持续更新的(应该). 「CF1521E」Nastia and a Beautiful Matrix Thought. 要把所有数 ...

  6. 【BZOJ3894】文理分科

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

  7. BZOJ3894:文理分科(最大流)(同BZoj3438)

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

  8. BZOJ3894:文理分科——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=3894 文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过) 小P所在的班级要进行文理 ...

  9. 【bzoj3894】文理分科 网络流最小割

    原文地址:http://www.cnblogs.com/GXZlegend 题目描述 文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过) 小P所在的班级要进行文理分科.他的班级可以用 ...

随机推荐

  1. 干货 | Keepalived高可用服务配置实例

    一个执着于技术的公众号 Keepalived系列导读 Keepalived入门学习 keepalived安装及配置文件详解 前言 在前面的章节中,我们学习了Keepalived简介.原理.以及Keep ...

  2. Barbican密钥管理器服务

    Barbican服务介绍 Key Manager 服务 (barbican) 提供机密数据的安全存储.配置和管理.这包括密钥材料,例如对称密钥.非对称密钥.证书和原始二进制数据. Barbican 是 ...

  3. 震惊--Nginx的map指令还能这样用

    map指令简单介绍 当然这里写的都是官方文档是已经写过的,我简单抄一下哈. map指令来自于 ngx_http_map_module 模块,提供的核心能力是 基于一个变量创建一个新变量,大概是这意思. ...

  4. 如何形象简单地理解java中只有值传递,而没有引用传递?

    首先,java中只有值传递,没有引用传递.可以说是"传递的引用(地址)",而不能说是"按引用传递". 按值传递意味着当将一个参数传递给一个函数时,函数接收的是原 ...

  5. 11┃音视频直播系统之 WebRTC 进行文本聊天并实时传输文件

    一.RTCDataChannel WebRTC 不但可以让你进行音视频通话,而且还可以用它传输普通的二进制数据,比如说可以利用它实现文本聊天.文件的传输等 WebRTC 的数据通道(RTCDataCh ...

  6. 好客租房56-props深入(3props校验-约束规则)

    1常见类型:Array,bool,func,number,object,string 2React:element 3必填项:isRequred 4特定结构的想:shape({}) //导入react ...

  7. 115_Power Pivot之HR薪酬计算:公积金、社保、个税、实发工资相关

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 1.之前写了一个关于入离调转的pp应用,现在个税新增专项附加扣除项目,借此写一个关于薪酬计算的案例: 2.本案例 ...

  8. 羿网通WT2100网络测试仪端口开关功能应用案例

    端口开关是羿网通WT2100具备的一项全局性的功能,使用客户端软件Packlark连接WT2100后无需进入具体功能即可使用.该功能是通过控制设备上的以太网开关实现快速.便捷地切换网口通断状态的目标, ...

  9. es的查询、排序查询、分页查询、布尔查询、查询结果过滤、高亮查询、聚合函数、python操作es

    今日内容概要 es的查询 Elasticsearch之排序查询 Elasticsearch之分页查询 Elasticsearch之布尔查询 Elasticsearch之查询结果过滤 Elasticse ...

  10. 题解 P7075 [CSP-S2020] 儒略日

    当时考场上因为这个炸掉,一年后回来复仇. 这里提供一个与大多数人不一样的做法. 首先考虑一个简单一些的问题,怎么应付单个询问? 不难想到,我们对于一个日期,让他从 \(-4713\) 年 \(1\) ...