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.

  1. #include <queue>
  2. #include <cstdio>
  3. using namespace std;
  4. int Abs(int x) { return x < 0 ? -x : x; }
  5. int Max(int x, int y) { return x > y ? x : y; }
  6. int Min(int x, int y) { return x < y ? x : y; }
  7. int read() {
  8. int x = 0, k = 1;
  9. char s = getchar();
  10. while(s < '0' || s > '9') {
  11. if(s == '-')
  12. k = -1;
  13. s = getchar();
  14. }
  15. while('0' <= s && s <= '9') {
  16. x = (x << 3) + (x << 1) + (s ^ 48);
  17. s = getchar();
  18. }
  19. return x * k;
  20. }
  21. void write(int x) {
  22. if(x < 0) {
  23. x = -x;
  24. putchar('-');
  25. }
  26. if(x > 9)
  27. write(x / 10);
  28. putchar(x % 10 + '0');
  29. }
  30. void print(int x, char s) {
  31. write(x);
  32. putchar(s);
  33. }
  34. const int MAXN = 1e2 + 5;
  35. const int MAXM = 1e5 + 4e4 + 5;
  36. const int MAXL = 3e4 + 5;
  37. const int INF = 2147483647;
  38. struct Maximum_flow {
  39. struct edge {
  40. int v, nxt;
  41. edge() {}
  42. edge(int V, int Nxt) {
  43. v = V, nxt = Nxt;
  44. }
  45. } e[MAXM << 1];
  46. int n, cnt, s, t;
  47. int Cap[MAXM << 1], Flow[MAXM << 1];
  48. int Lab[MAXL], Cur[MAXL], head[MAXL];
  49. queue<int> q;
  50. void init(int N, int S, int T) {
  51. for(int i = 0; i <= cnt; i++)
  52. Flow[i] = 0, Cap[i] = 0;
  53. n = N, cnt = 0, s = S, t = T;
  54. for(int i = 1; i <= n; i++)
  55. head[i] = -1;
  56. }
  57. void Add_Edge(int u, int v, int w) {
  58. Cap[cnt] += w;
  59. e[cnt] = edge(v, head[u]);
  60. head[u] = cnt++;
  61. e[cnt] = edge(u, head[v]);
  62. head[v] = cnt++;
  63. }
  64. bool Lab_Vertex() {
  65. for(int i = 1; i <= n; i++)
  66. Lab[i] = 0;
  67. Lab[t] = 1;
  68. while(!q.empty())
  69. q.pop();
  70. q.push(t);
  71. while(!q.empty()) {
  72. int v = q.front();
  73. q.pop();
  74. for(int i = head[v], u; ~i; i = e[i].nxt) {
  75. u = e[i].v;
  76. if(!Lab[u] && Cap[i ^ 1] - Flow[i ^ 1]) {
  77. Lab[u] = Lab[v] + 1;
  78. q.push(u);
  79. if(u == s)
  80. return Lab[s];
  81. }
  82. }
  83. }
  84. return Lab[s];
  85. }
  86. int Widen(int u, int Limit) {
  87. if(u == t)
  88. return Limit;
  89. int Used = 0, Delta;
  90. for(int i = Cur[u], v; ~i; i = e[i].nxt) {
  91. v = e[i].v;
  92. Cur[u] = i;
  93. if(Lab[v] + 1 != Lab[u] || Cap[i] - Flow[i] <= 0)
  94. continue;
  95. Delta = Widen(v, Min(Limit - Used, Cap[i] - Flow[i]));
  96. Used += Delta, Flow[i] += Delta, Flow[i ^ 1] -= Delta;
  97. if(Used == Limit)
  98. return Used;
  99. }
  100. return Used;
  101. }
  102. int Dinic() {
  103. int res = 0;
  104. while(Lab_Vertex()) {
  105. for(int i = 1; i <= n; i++)
  106. Cur[i] = head[i];
  107. res += Widen(s, INF);
  108. if(res < 0)
  109. return INF;
  110. }
  111. return res;
  112. }
  113. } Flow_Graph;
  114. int dir[5][2] = {{0, 0}, {0, 1}, {1, 0}, {-1, 0}, {0, -1}};
  115. int a[MAXN][MAXN][2], s[MAXN][MAXN][2], n, m;
  116. int Get(int x, int y) { return (x - 1) * m + y; }
  117. int main() {
  118. n = read(), m = read();
  119. int Sum = 0;
  120. for(int i = 1; i <= n; i++)
  121. for(int j = 1; j <= m; j++)
  122. a[i][j][0] = read();
  123. for(int i = 1; i <= n; i++)
  124. for(int j = 1; j <= m; j++)
  125. s[i][j][0] = read();
  126. for(int i = 1; i <= n; i++)
  127. for(int j = 1; j <= m; j++)
  128. a[i][j][1] = read();
  129. for(int i = 1; i <= n; i++)
  130. for(int j = 1; j <= m; j++)
  131. s[i][j][1] = read();
  132. int S = n * m * 3 + 1, T = n * m * 3 + 2;
  133. Flow_Graph.init(T, S, T);
  134. for(int i = 1; i <= n; i++)
  135. for(int j = 1, Pos; j <= m; j++) {
  136. Pos = Get(i, j);
  137. Sum += a[i][j][0], Sum += a[i][j][1], Sum += s[i][j][0], Sum += s[i][j][1];
  138. Flow_Graph.Add_Edge(S, Pos, a[i][j][0]);
  139. Flow_Graph.Add_Edge(Pos, T, s[i][j][0]);
  140. Flow_Graph.Add_Edge(S, Pos + n * m, a[i][j][1]);
  141. Flow_Graph.Add_Edge(Pos + n * m * 2, T, s[i][j][1]);
  142. for(int k = 0, x, y; k < 5; k++) {
  143. x = i + dir[k][0], y = j + dir[k][1];
  144. if(x > n || x < 1 || y > m | y < 1)
  145. continue;
  146. Flow_Graph.Add_Edge(Pos + n * m, Get(x, y), INF);
  147. Flow_Graph.Add_Edge(Get(x, y), Pos + n * m * 2, INF);
  148. }
  149. }
  150. print(Sum - Flow_Graph.Dinic(), '\n');
  151. return 0;
  152. }

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. 劳动节快乐!手写个核心价值观编码工具 - Python实现

    前言 今天是五一劳动节,祝各位无产阶级劳动者节日快乐! 然后来整活分享一些有趣的东西~ 这个小工具是我大学时做着玩的,对于各位接班人来说,12个词的核心价值观这东西,大家都非常熟悉了,这工具可以实现将 ...

  2. LeetCode数组刷题——448、48、240、769

    1.[LeetCode448]:448. 找到所有数组中消失的数字 题目分析: 1-n之间有重复的,有没出现的,有出现一次.使用hashmap,空间复杂度为O(n) 方法一:哈希表,但是空间复杂度超过 ...

  3. 图文详解:小白也能看懂的 Kubernetes

    Kubernetes 这个单词来自于希腊语,含义是舵手或领航员 .其词根是 governor 和 cybernetic.K8s 是它的缩写,用 8 字替代了"ubernete". ...

  4. 手脱NsPacK壳

    1.查壳 使用PEiD未能检测到壳信息,这时,我们更换其他工具 从图中可以看到壳的信息为[NsPacK(3.x)[-]] 2.百度壳信息 北斗程序压缩(Nspack)是一款压缩壳.主要的选项是:压缩资 ...

  5. Rainbond结合NeuVector实践容器安全管理

    前言 Rainbond 是一个云原生应用管理平台,使用简单,不需要懂容器.Kubernetes和底层复杂技术,支持管理多个Kubernetes集群,和管理企业应用全生命周期.但是随着云原生时代的一点点 ...

  6. linux篇--mysql数据库备份并删除前一分钟的数据

    linux 中mysql数据库定时备份并删除前一分钟的所有数据 #!/bin/bash #mysqldump -uroot -ppassword01! imaginebase > /home/b ...

  7. javaweb开发案例

    1.实验3 (1)当运行Servlet时,碰到"空指针异常"错误怎么处理? 答:应提示用户操作有误,或设置对象值为空字符串或一个默认值,或是不执行某操作,直接跳转到其他处理中. ( ...

  8. map计算

    map理解 参考1: https://github.com/rafaelpadilla/Object-Detection-Metrics 参考2:https://github.com/rafaelpa ...

  9. 定制ASP.NET 6.0的应用配置

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本文的主题是应用程序配置.要介绍的是如何使用配置.如何自定义配置,以采用不同的方式 ...

  10. 《HALCON数字图像处理》第一、二章笔记

    目录 第一章 绪论 1.1 图像和图像处理 1.1.1 图像 1.1.2 数字图像 1.1.3 图像处理及其发展过程 1.2 数字图像处理的步骤和方法 1.3 数字图像处理系统的硬件组成 1.4 数字 ...