题目大意:在一个有 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. cocos2d-x https

    cocos2d-x :2.1.3HttpClient.cpp文件中  bool configureCURL(CURL *handle)后边添加如下两句: curl_easy_setopt(handle ...

  2. 【HTTP】如何正常关闭连接

    参考:<HTTP权威指南> 所有HTTP客户端.服务器或者代理都可以任意时刻关闭一条TCP传输连接.但是服务器永远无法确定它关闭“空闲”连接的那一刻,在线路那一头的客户端有没有数据要发送. ...

  3. 如何通过WallpaperManager(壁纸管理器)设置与修改壁纸?

    WallpaperManager(壁纸管理器),是手机壁纸相关的一个API的相关类.其设置壁纸常有如下三种方法可调用: - setBitmap(Bitmap bitmap):将壁纸设置为bitmap所 ...

  4. Reducing the Dimensionality of Data with Neural Networks:神经网络用于降维

    原文链接:http://www.ncbi.nlm.nih.gov/pubmed/16873662/ G. E. Hinton* and R. R. Salakhutdinov .   Science. ...

  5. Vs2010删除空白行

    在copy了别人的代码之后,出现了大量的空白行,批量删除空白行方法为: 查找内容:^:b*$\n 替换为: 查找范围:当前文档 使用:正则表达式

  6. react基础篇一

    jsx简介 const element = <h1>Hello, world!</h1>; 这种看起来可能有些奇怪的标签语法既不是字符串也不是 HTML. 它被称为 JSX, ...

  7. 02 java学习安装jdk及其环境配置

    SUN公司1995年正式推出的一款语言 其实之前,Sun公司1991年,James Gosling等人就开始开发Oak语言,希望用于控制嵌入有效电视交换盒,1994年更名为Java,之前来自 与jav ...

  8. javascript匿名函数及闭包深入理解及应用

    1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种: ...

  9. BZOJ 1060: [ZJOI2007]时态同步 树上问题 + 贪心

    Description 小Q在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数 字1,2,3….进行标号.电路板的各个节点由若干不相交的导线相连接,且对于电路 ...

  10. vue组件的拆分

    vue组件的拆分 <div class="div"> <!-- 拆分出来的组件 自定义名字moban --> <moban></moban ...