luogu2774 方格取数问题 二分图最小权点覆盖集
题目大意:在一个有 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 方格取数问题 二分图最小权点覆盖集的更多相关文章
- BZOJ 1475 方格取数(二分图最大点权独立集)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1475 [题目大意] 给出一个n*n的方格,从中取一些不相邻的数字,使得和最大 [题解] ...
- HDU1569 方格取数(2) —— 二分图点带权最大独立集、最小割最大流
题目链接:https://vjudge.net/problem/HDU-1569 方格取数(2) Time Limit: 10000/5000 MS (Java/Others) Memory L ...
- hdu 3657 最大点权独立集变形(方格取数的变形最小割,对于最小割建图很好的题)
转载:http://blog.csdn.net/cold__v__moon/article/details/7924269 /* 这道题和方格取数2相似,是在方格取数2的基础上的变形. 方格取数2解法 ...
- P2774 方格取数问题(最小割)
P2774 方格取数问题 一看题目便知是网络流,但由于无法建图.... 题目直说禁止那些条件,这导致我们直接建图做不到,既然如此,我们这是就要逆向思维,他禁止那些边,我们就连那些边. 我们将棋盘染色, ...
- 洛谷 - P2774 - 方格取数问题 - 二分图最大独立点集 - 最小割
https://www.luogu.org/problemnew/show/P2774 把两个相邻的节点连边,这些边就是要方便最小割割断其他边存在的,容量无穷. 这种类似的问题的话,把二分图的一部分( ...
- 【Codevs1907】方格取数3(最小割)
题意:在一个有m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法. n,m<=30 思路:如果 ...
- 【PowerOJ1744&网络流24题】方格取数问题(最小割)
题意: n,m<=30 思路: [问题分析] 二分图点权最大独立集,转化为最小割模型,从而用最大流解决. [建模方法] 首先把棋盘黑白染色,使相邻格子颜色不同,所有黑色格子看做二分图X集合中顶点 ...
- [luoguP2774] 方格取数问题(最大点权独立集)
传送门 引入两个概念: 最小点权覆盖集:满足每一条边的两个端点至少选一个的最小权点集. 最大点权独立集:满足每一条边的两个端点最多选一个的最大权点集. 现在对网格染色,使得相邻两点颜色不同,之后把两个 ...
- HDU 1565:方格取数(1)(最大点权独立集)***
http://acm.hdu.edu.cn/showproblem.php?pid=1565 题意:中文. 思路:一个棋盘,要使得相邻的点不能同时选,问最大和是多少,这个问题就是最大点权独立集. 可以 ...
随机推荐
- 【PostgreSQL-9.6.3】psql常用命令
命令 描述 \l 查看数据库 \c 换库 \d 查看所有表 \dt 只显示匹配的表 \di 只显示匹配的索引 \ds 只显示匹配的序列 \dv 只显示匹配的视图 \df 只显示匹配的函数 \d t1 ...
- 2013款MacBook Air装Windows7单系统
经过两天的摸索,查找无数资料终于把2013款的MacBook Air装上了WIN 7,虽然网上有很多的资料但是都不是我想要的,第一个我的是2013款的MacBook Air,跟原来2012 11款Ma ...
- SSDB 一个高性能的支持丰富数据结构的 NoSQL 数据库, 用于替代 Redis.
SSDB 一个高性能的支持丰富数据结构的 NoSQL 数据库, 用于替代 Redis. 特性 替代 Redis 数据库, Redis 的 100 倍容量 LevelDB 网络支持, 使用 C/C++ ...
- 三维重建7:Visual SLAM算法笔记
VSLAM研究了几十年,新的东西不是很多,三维重建的VSLAM方法可以用一篇文章总结一下. 此文是一个好的视觉SLAM综述,对视觉SLAM总结比较全面,是SLAM那本书的很好的补充.介绍了基于滤波器的 ...
- 时序分析:串匹配-KMP算法
图像处理与模式识别的教科书使用大量的章节来描述空域的模式识别方法.从图像底层特征提取.贝叶斯方法到多层神经网络方法,一般不讨论到对象随时间变化的情况,视频处理应用和在线学习方法使研究对象开始向时域延伸 ...
- SLAM: Ubuntu14.04_Kylin安装ROS-Indigo
参考连接:ROS-Indigo版在Ubuntu上的安装第一步: 软件源配置 1. 增加下载源(增加ubuntu版的ros数据仓库,即下载源)(通用指令适合任何版本的ros) sudo sh -c 'e ...
- (转)基于Metronic的Bootstrap开发框架经验总结(3)--下拉列表Select2插件的使用
http://www.cnblogs.com/wuhuacong/p/4761637.html 在上篇<基于Metronic的Bootstrap开发框架经验总结(2)--列表分页处理和插件JST ...
- Django的Error汇总
title: Django学习笔记 catalog: true subtitle: 11. Django_Error汇总 date: 2018-12-14 10:17:28 --- Django的Er ...
- Python基础知识-Day4
一.函数关键字关键字是Python内置的,具有特殊意义的标识符,自定义标识符命名时不可与之重复.可以通过以下代码查看Python内置的关键字内容. import keyword print(keywo ...
- 01-Linux命令基础-第01天(命令基础,软件安装与卸载、磁盘管理)
01- Linux初步 最早一直是单道程序设计模型的操作系统 69年贝尔实验室决定开发多道程序设计模型的操作系统 Multics计划 (失败了) x86 IA(Intel Architecture ...