[BZOJ 3774] 最优选择 【最小割】

题目链接:BZOJ - 3774

题目分析

此题与“文理分科”那道题目有些类似。都是使用最小割来求解,先加上可能获得的权值,在减掉必须舍弃的权值(最小割)。

文理分科是规定每个人和 S 连就是选文,和 T 连就是选理。然后如果一个人和相邻的人都全文就会获得一个权值,那么我们就为这个权值建一个点,让这个点与必须同时选文的5个人连 INF 边。这样只要这 5 个人中有一个人选了理,就必须舍弃这个权值了。

再回到这道题目,这道题获得权值的条件是这个点被控制或这个点相邻的 4 个点都被控制。 这个“或”并不太好处理,我们就把这个条件拆成两个不相交的条件:

1)这个点被控制,可以获得权值。

2)这个点没有被控制且相邻的4个点都被控制,可以获得权值。

这样的话第一个条件就是不控制这个点需要付出的代价,第二个条件是“这个点没有被控制且相邻的4个点都被控制”,只要有一个点不符合就要割掉这个权值。

但是这些需要同时满足的条件有“被控制”和“不被控制”,直接用“文理分科”的建图方式是方向不一致的。

所以我们利用矩阵可以黑白染色成为二分图的性质,将矩阵的格子黑白染色之后,对于白点和黑点用相反的方式连边,这样黑点的“被控制”和白点的“不被控制”就是一个方向的了。

注意刚开始时预先加到答案里的权值是给定权值的两倍,因为我们将权值分成了两种情况。

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

inline int gmin(int a, int b) {return a < b ? a : b;}

const int MaxMap = 50 + 5, MaxN = 5000 + 5, MaxM = 100000 + 5, INF = 999999999;
const int Dx[5] = {0, 0, 1, -1}, Dy[5] = {1, -1, 0, 0};

int n, m, nm, Tot, Ans, S, T;
int f[MaxMap][MaxMap], A[MaxMap][MaxMap], B[MaxMap][MaxMap], d[MaxN], Num[MaxN];

struct Edge
{
	int v, w;
	Edge *Next, *Other;
} E[MaxM], *P = E, *Point[MaxN], *Last[MaxN];

inline void AddEdge(int x, int y, int z)
{
	Edge *Q = ++P; ++P;
	P -> v = y; P -> w = z;
	P -> Next = Point[x]; Point[x] = P; P -> Other = Q;
	Q -> v = x; Q -> w = 0;
	Q -> Next = Point[y]; Point[y] = Q; Q -> Other = P;
}

inline bool Inside(int x, int y)
{
	if (x < 1 || x > n) return false;
	if (y < 1 || y > m) return false;
	return true;
}

int DFS(int Now, int Flow)
{
	if (Now == T) return Flow;
	int ret = 0;
	for (Edge *j = Last[Now]; j; j = j -> Next)
		if (j -> w && d[Now] == d[j -> v] + 1)
		{
			Last[Now] = j;
			int p = DFS(j -> v, gmin(Flow - ret, j -> w));
			ret += p; j -> w -= p; j -> Other -> w += p;
			if (ret == Flow) return ret;
		}
	if (d[S] >= Tot) return ret;
	if (--Num[d[Now]] == 0) d[S] = Tot;
	++Num[++d[Now]];
	Last[Now] = Point[Now];
	return ret;
}

int main()
{
	scanf("%d%d", &n, &m);
	nm = n * m;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			f[i][j] = (i - 1) * m + j;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			scanf("%d", &A[i][j]);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
		{
			scanf("%d", &B[i][j]);
			Ans += B[i][j] * 2;
		}
	Tot = 2 * nm; S = ++Tot; T = ++Tot;
	int x, y;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
		{
			if ((i + j) & 1)
			{
				AddEdge(f[i][j], T, A[i][j]);
				AddEdge(S, f[i][j], B[i][j]);
				AddEdge(nm + f[i][j], T, B[i][j]);
				AddEdge(f[i][j], nm + f[i][j], INF);
				for (int k = 0; k < 4; ++k)
				{
					x = i + Dx[k]; y = j + Dy[k];
					if (!Inside(x, y)) continue;
					AddEdge(f[x][y], nm + f[i][j], INF);
				}
			}
			else
			{
				AddEdge(S, f[i][j], A[i][j]);
				AddEdge(f[i][j], T, B[i][j]);
				AddEdge(S, nm + f[i][j], B[i][j]);
				AddEdge(nm + f[i][j], f[i][j], INF);
				for (int k = 0; k < 4; ++k)
				{
					x = i + Dx[k]; y = j + Dy[k];
					if (!Inside(x, y)) continue;
					AddEdge(nm + f[i][j], f[x][y], INF);
				}
			}
		}
	memset(d, 0, sizeof(d));
	memset(Num, 0, sizeof(Num)); Num[0] = Tot;
	for (int i = 1; i <= Tot; ++i) Last[i] = Point[i];
	while (d[S] < Tot) Ans -= DFS(S, INF);
	printf("%d\n", Ans);
	return 0;
}

  

时间: 2024-11-06 03:33:05

[BZOJ 3774] 最优选择 【最小割】的相关文章

BZOJ 3774: 最优选择( 最小割 )

最小割...二分染色然后把颜色不同的点的源汇反过来..然后就可以做了. 某个点(x,y): S->Id(x,y)(回报), Id(x,y)->T(代价), Id(i,j)&&Id(相邻节点)->newId(i,j)(+oo), newId(i,j)->T(回报) 然后染色不同的点反过来就可以了. 初始时答案为2*∑回报, 这样每个点要么割掉1个回报,要么割掉2个回报, 要么割掉1回报+代价.都对应着每一种方案 ----------------------------

【BZOJ3774】最优选择 最小割

[BZOJ3774]最优选择 Description 小N手上有一个N*M的方格图,控制某一个点要付出Aij的代价,然后某个点如果被控制了,或者他周围的所有点(上下左右)都被控制了,那么他就算是被选择了的.一个点如果被选择了,那么可以得到Bij的回报,现在请你帮小N选一个最优的方案,使得回报-代价尽可能大. Input 第一行两个正整数N,M表示方格图的长与宽. 接下来N行每行M个整数Aij表示控制的代价. 接下来N行每行M个整数Bij表示选择的回报. Output 一个整数,表示最大的回报-代

BZOJ 2007 海拔(平面图最小割-最短路)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2007 题意:给出一个n*n的格子,那么顶点显然有(n+1)*(n+1)个.每两个相邻顶点之间有两条边,这两条边是有向的,边上有权值..左上角为源点,右下角为汇点,求s到t的最小割. 思路:很明显这是一个平面图,将其转化为最 短路.我们将s到t之间连一条边,左下角为新图的源点S,右上角区域为新图的终点T,并且为每个格子编号.由于边是有向的,我们就要分析下这条边应该是哪 个点向哪个点的边.

ZOJ 2676 Network Wars(最优比例最小割)

Network Wars Time Limit: 5 Seconds      Memory Limit: 32768 KB      Special Judge Network of Byteland consists of n servers, connected by m optical cables. Each cable connects two servers and can transmit data in both directions. Two servers of the n

BZOJ 2132 圈地计划 最小割

题目大意:给定一个m*n的矩阵,每个位置如果作为商业区或者工业区各有一个收益,如果相邻两块是不同的也会有一个收益,求最大收益 吐槽:住宅区呢- - 地理老师骗我们- - 普通的最小割建图会遇到一个问题: 割断两块之间的边收益为正,即代价为负 因此我们如果正常建最小割,那么两块之间的边权就会是负的 那么我们将这个矩阵黑白染色,将白格ST反向 这样割断两块之间的连边相当于两块选择了同一用途,代价为正 就可以正常跑了 #include <cstdio> #include <cstring>

[BZOJ 3144] [Hnoi2013] 切糕 【最小割】

题目链接:BZOJ - 3144 题目分析 题意:在 P * Q 的方格上填数字,可以填 [1, R] . 在 (x, y) 上填 z 会有 V[x][y][z] 的代价.限制:相邻两个格子填的数字的差的绝对值不能超过 D . 求一个合法的最小总代价. 这道题是一个最小割模型,直接说建图吧. 建图:每个点 (x, y) 拆成 R 个点,(x, y, z) 代表 (x, y) 填 z. 然后从 S 向 (*, *, 1) 连 INF ,从 (*, *, R) 向 T 连 INF . 然后对于 (i

[BZOJ 3894] 文理分科 【最小割】

题目链接:BZOJ - 3894 题目分析 最小割模型,设定一个点与 S 相连表示选文,与 T 相连表示选理. 那么首先要加上所有可能获得的权值,然后减去最小割,即不能获得的权值. 那么对于每个点,从 S 向它连权值为它选文的价值的边,从它向 T 连权值为它选理的价值的边. 对于一个点,它和与它相邻的点构成了一个集合,这个集合如果都选文,可以获得一个价值v1,如果都选理,可以获得一个价值 v2. 只要这个集合中有一个点选文,就无法获得 v2,只要有一个点选理,就无法获得 v1. 那么处理方式就是

BZOJ 3144 HNOI 2013 切糕 最小割

题目大意:给出一个三维的点阵,没个点都有可能被切割,代价就是这个点的权值.相邻的切割点的高度差不能超过D,问最小的花费使得上下分开. 思路:很裸的最小割模型,很神的建图. S->第一层的点,f:INF 所有点->它下面的点,f:INF 一个点的入->一个点的出,f:val[i] (i,j,k) - > (i - d,j,k),f:INF 最下面一层的点->T:f:INF 然后跑最小割就是答案. 为什么见:http://www.cnblogs.com/zyfzyf/p/4182

BZOJ 3275 Number &amp;&amp; 3158 千钧一发 最小割

题目大意:给出一些数字,要求选出一些数字并保证所有数字和最大,要求这其中的数字任意两个至少满足一个条件,则不能同时被选:1.这两个数的平方和是完全平方数.2.gcd(a,b) = 1. 思路:我们可以将奇数和偶数分开来讨论,奇数不满足1,偶数不满足2,所以奇数和奇数,偶数和偶数不会互相影响.之后O(n^2)的讨论其他数字对,有影响就连边,流量正无穷,最后跑最小割最最大获利. CODE: #define _CRT_SECURE_NO_WARNINGS #include <cmath> #incl