题目大意:在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,输出这些数之和的最大值。

思路:这种各个点之间互相排斥求最大值的题,往往需要利用上网络流最小割的性质。我们把方格中的所有数字都选上,看看把哪些格子抠掉,能使数值和的减少量最少。

每个格子看作一个节点,其向四周的格子代表的节点连边。现要求一个节点的集合,使得这些点与所有边相连,求点权之和最小值。这就是最小权点覆盖集问题。

要想使该问题有解,往往要将图中的节点分为两个集合,一个集合内的任意两个节点之间没有边相连,也就是说图中所有边两头的节点必须属于不同的集合。解决该问题方法为将S与一个集合中的所有点相连,将另一个集合中的所有点与汇点相连,原图中的边容量设为无穷大,然后跑一遍最大流即可。

理解:每条连接两个集合的边两边的节点我们只要选一个抠掉,两个集合即可彻底分开。这条边的容量是无穷大,即可保证两个 连接两个集合的边的两端节点 与 源汇 连的边中 只会容量小的那个满流,表示选择了这条节点。这样,此时的最大流(最小割)便是最小点权之和。

本题中,我们发现如果把方格按照国际象棋棋盘那样的方式决定节点的集合,恰好满足要求。

#include <cstdio>
#include <cassert>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std; #define LOOP(i, n) for(int i=1; i<=n; i++)
const int MAX_NODE = , MAX_EDGE = MAX_NODE * , INF = 0x3f3f3f3f; struct Dinic
{
struct Node;
struct Edge; struct Node
{
Edge *Head, *DfsFrom;
int Level;
}_nodes[MAX_NODE];
int _vCount;
Node *Start, *Target; struct Edge
{
Node *To;
Edge *Next, *Rev;
int Cap;
Edge(Node *to, Edge *next, int cap):To(to),Next(next),Cap(cap){}
}*_edges[MAX_EDGE];
int _eCount; void Init(int vCount, int sId, int tId)
{
_vCount = vCount;
Start = sId + _nodes;
Target = tId + _nodes;
_eCount = ;
} Edge *AddEdge(Node *from, Node *to, int cap)
{
Edge *e = _edges[++_eCount] = new Edge(to, from->Head, cap);
from->Head = e;
return e;
} void Build(int uId, int vId, int cap)
{
Node *u = uId + _nodes, *v = vId + _nodes;
Edge *e1 = AddEdge(u, v, cap), *e2 = AddEdge(v, u, );
e1->Rev = e2;
e2->Rev = e1;
} bool Bfs()
{
static queue<Node*> q;
LOOP(i, _vCount)
_nodes[i].Level = ;
Start->Level = ;
q.push(Start);
while (!q.empty())
{
Node *u = q.front();
q.pop();
for (Edge *e = u->Head; e; e = e->Next)
{
if (!e->To->Level && e->Cap)
{
e->To->Level = u->Level + ;
q.push(e->To);
}
}
}
return Target->Level;
} int Dfs(Node *cur, int limit)
{
if (cur == Target)
return limit;
if (limit == )
return ;
int curTake = ;
for (Edge *e = cur->DfsFrom; e; cur->DfsFrom = e = e->Next)
{
if (e->To->Level == cur->Level + && e->Cap)
{
int nextTake = Dfs(e->To, min(limit - curTake, e->Cap));
e->Cap -= nextTake;
e->Rev->Cap += nextTake;
curTake += nextTake;
}
if (limit - curTake==)
break;
}
return curTake;
} int Proceed()
{
int ans = ;
while (Bfs())
{
LOOP(i, _vCount)
_nodes[i].DfsFrom = _nodes[i].Head;
ans += Dfs(Start, INF);
}
return ans;
}
}g; int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
const int direct[][] = { {-,},{,},{,},{,-} };
int totCol, totRow, sId, tId, totSum = ;
static int matrix[*];
scanf("%d%d", &totCol, &totRow);
sId = totCol*totRow + ;
tId = totCol*totRow + ;
g.Init(tId, sId, tId);
LOOP(col, totCol)
LOOP(row, totRow)
{
int cur = (col - )*totRow + row;
scanf("%d", &matrix[cur]);
totSum += matrix[cur];
if ((row + col - ) % )
g.Build(sId, cur, matrix[cur]);
else
g.Build(cur, tId, matrix[cur]);
}
LOOP(col, totCol)
LOOP(row, totRow)
if ((row + col - ) % )
{
int cur = (col - )*totRow + row;
for (int i = ; i < ; i++)
{
int col2 = col + direct[i][], row2 = row + direct[i][];
if (col2 >= && col2 <= totCol&&row2 >= && row2 <= totRow)
{
int next = totRow*(col2 - ) + row2;
g.Build(cur, totRow*(col2 - ) + row2, INF);
}
}
}
int subt = g.Proceed();
printf("%d\n", totSum - subt);
return ;
}

注意:

1.判断方格的上下左右这方面,尽量分别用两个变量表示行和列。直观,不容易出错。

2.先系统连与源汇相连的边,再连两个集合间的边。错误做法:站在一个集合上,找能连到另一个集合的节点的边,将其构造,然后将令那个另一个集合的节点与汇点相连。因为一个集合的点有多条边,每次把另一个集合的节点与汇点相连造成了很多重边。

luogu2774 方格取数问题 二分图最小权点覆盖集的更多相关文章

  1. BZOJ 1475 方格取数(二分图最大点权独立集)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1475 [题目大意] 给出一个n*n的方格,从中取一些不相邻的数字,使得和最大 [题解] ...

  2. HDU1569 方格取数(2) —— 二分图点带权最大独立集、最小割最大流

    题目链接:https://vjudge.net/problem/HDU-1569 方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory L ...

  3. hdu 3657 最大点权独立集变形(方格取数的变形最小割,对于最小割建图很好的题)

    转载:http://blog.csdn.net/cold__v__moon/article/details/7924269 /* 这道题和方格取数2相似,是在方格取数2的基础上的变形. 方格取数2解法 ...

  4. P2774 方格取数问题(最小割)

    P2774 方格取数问题 一看题目便知是网络流,但由于无法建图.... 题目直说禁止那些条件,这导致我们直接建图做不到,既然如此,我们这是就要逆向思维,他禁止那些边,我们就连那些边. 我们将棋盘染色, ...

  5. 洛谷 - P2774 - 方格取数问题 - 二分图最大独立点集 - 最小割

    https://www.luogu.org/problemnew/show/P2774 把两个相邻的节点连边,这些边就是要方便最小割割断其他边存在的,容量无穷. 这种类似的问题的话,把二分图的一部分( ...

  6. 【Codevs1907】方格取数3(最小割)

    题意:在一个有m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法. n,m<=30 思路:如果 ...

  7. 【PowerOJ1744&网络流24题】方格取数问题(最小割)

    题意: n,m<=30 思路: [问题分析] 二分图点权最大独立集,转化为最小割模型,从而用最大流解决. [建模方法] 首先把棋盘黑白染色,使相邻格子颜色不同,所有黑色格子看做二分图X集合中顶点 ...

  8. [luoguP2774] 方格取数问题(最大点权独立集)

    传送门 引入两个概念: 最小点权覆盖集:满足每一条边的两个端点至少选一个的最小权点集. 最大点权独立集:满足每一条边的两个端点最多选一个的最大权点集. 现在对网格染色,使得相邻两点颜色不同,之后把两个 ...

  9. HDU 1565:方格取数(1)(最大点权独立集)***

    http://acm.hdu.edu.cn/showproblem.php?pid=1565 题意:中文. 思路:一个棋盘,要使得相邻的点不能同时选,问最大和是多少,这个问题就是最大点权独立集. 可以 ...

随机推荐

  1. A - HQ9+

    Problem description HQ9+ is a joke programming language which has only four one-character instructio ...

  2. java实现读取yaml文件,并获取值

    首先在项目src目录下新建一个test.yaml的文件. 代码如下: spring: application: name: cruncher datasource: driverClassName: ...

  3. dubbo之分组聚合

    按组合并返回结果 ,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项. 相关代码可以参考 dubbo 项目 ...

  4. Vue2 封装的 Quill 富文本编辑器组件 Vue-Quill-Editor

    1.安装 npm install vue-quill-editor --save 2.使用 import { quillEditor } from 'vue-quill-editor' 3.组件中 & ...

  5. Js构造对象-添加方法的三种方式

    Js构造函数添加方法有多种方案,来看一个混合方式构造函数的例子:申明person构造函数,有两个属性,name,qq.在原型上添加方法showname.这是最常用的方法. <script> ...

  6. 构造函数+原型的js混合模式

    function Parent(){ this.name = "李小龙"; this.age = "30"; };Parent.prototype.lev=fu ...

  7. 【剑指Offer】20、包含min函数的栈

      题目描述:   定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1)).   解题思路:   使用两个stack,一个为数据栈,另一个为辅助栈.数据栈 ...

  8. bzoj4320 homework 题解

    题面链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4320 令M=sqrt(mx),把询问的Y按M 分成两种不同的处理方式. 1.对于> ...

  9. 360 基于 Prometheus的在线服务监控实践

    转自:https://mp.weixin.qq.com/s/lcjZzjptxrUBN1999k_rXw 主题简介: Prometheus基础介绍 Prometheus打点及查询技巧 Promethe ...

  10. 多文件编程(day13)

    多文件编程时一个文件里可以包含多个函数, 一个函数只能属于一个文件 多文件编程的步骤 .把所有函数分散在多个不同的源文件里 (主函数通常单独占一个文件) .为每个源文件编写一个配对的以.h作为 扩展名 ...