题目大意:在一个有 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. 文件的上传(可以上传照片,word文档,等单个文件)

    jsp: jsp页面: <LINK href="${basePath}plugins/uploadify/uploadify.css" type="text/css ...

  2. Android开发笔记(12)——ListView & Adapter

    转载请注明:http://www.cnblogs.com/igoslly/p/6947225.html 下一章是关于ListFragment的内容,首先先介绍ListView的相关配置,理解ListF ...

  3. 安卓桌布显示的dip和px

    安卓程序设计界面显示设置图像大小,在layout.xml里面有dip和px选项,dip为 什么 暂时还不知道,或许是设计桌布的设定像素比率,px为像素值: 比如我的手机是 Lenovo K920,屏幕 ...

  4. 利用string 字符串拷贝

    序言:对于laws的代码,完全从Matlab中转来.其中用到了字符串复制和对比的函数. C++要求: 输入字符串,根据字符串,来确定选择数组,用于下一过程 MatLab代码: (1).文件calLaw ...

  5. 错误 RC1015: 无法打开包含文件 'XTToolkitPro.rc'

    XtremeToolkitPro作为VC++/MFC平台下目前最流行的GUI界面库之一,安装和使用都很方便.不过,在实际使用中还是碰到许多问题.如果在编译工程时只显示一个错误:RC1015: 无法打开 ...

  6. Linux各发行版配置总结

    Ubuntu 14.04(该版本下亲测好用) 1.修改开机启动项和默认启动项 sudo gedit /boot/grub/grub.cfg(修改前最好备份一个) 2.登陆界面图片的修改 /usr/sh ...

  7. Bem命名

    BEM思想 1. 什么是BEM: BEM:(Block块,Element元素,Modifier修饰符)一种命名规范, 其核心思想是将页面拆分成一个个独立的富有语义的块(blocks),从而使得团队在开 ...

  8. 【转载】Intellij IDEA的Hibernate简单应用

    转载自: https://www.cnblogs.com/yangyquin/p/5438248.html   1.创建数据库及其表 create database demo;    use demo ...

  9. commons.dbutils 的使用列子

    c0p3的导入请参考前文 https://www.cnblogs.com/appium/p/10183016.html JdbcUtils: package cn.itcast.jdbc; impor ...

  10. 【剑指Offer】44、反转单词序列

      题目描述:   牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思.例如,&qu ...