P4380 [USACO18OPEN]Multiplayer Moo

乍看题目,感觉不难写,处理图,把块变成节点,搜索,结果处理建图的时候越写越麻烦,没思路,只好抄题解了

用并查集的能理解,还有的搜索写了些剪枝,不理解。就放抄的并查集题解吧。

/*
每个数字块是一个整体,用并查集维护大小 。
给所有相邻数字块建边 ,并根据颜色排序 
处理每个数字块与那些数字块相连,维护一个颜色连接着 哪些数字块。
枚举处理所有相邻的数字块方案,已经根据数字值安排在一起。 
 
*/ 
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <vector>
#include<numeric>
using namespace std;

struct Edge {
	int u, v, c1, c2;
	Edge() {}
	Edge(int u, int v, int c1, int c2) : u(u), v(v), c1(min(c1, c2)), c2(max(c1, c2)) {}
	bool operator<(const Edge& rhs) const {
		if (c1 != rhs.c1)
			return c1 < rhs.c1;
		return c2 < rhs.c2;
	}
};

const int N = 252;

int n, ecnt;
int f[N * N], sz[N * N], szcp[N * N];
int a[N][N];
Edge ed[N * N * 2];
vector<int> sts[1000010];

int find(int x);
int gind(int x, int y);
void merge(int x, int y);

int main() {
	int a1 = 0, a2 = 0;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			scanf("%d", &a[i][j]);
	for(int i=1; i<=n*n; i++)	f[i]=i;
	for(int i=1; i<=n*n; i++)	sz[i]=1;

	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {//向右下方合并相同的区域 
			if (i != n && a[i][j] == a[i + 1][j])
				merge(gind(i, j), gind(i + 1, j));
			if (j != n && a[i][j] == a[i][j + 1])
				merge(gind(i, j), gind(i, j + 1));
			a1 = max(a1, sz[find(gind(i, j))]);//合并的过程中找最大值 
		}
	}
	for (int i = 1; i <= n; ++i)//再扫描一遍,找相邻的两个区域 
		for (int j = 1; j <= n; ++j) {
			if (i != n && a[i][j] != a[i + 1][j]) {//建右侧边 ,二维坐标转换为一维数字。 
				ed[++ecnt] = Edge(find(gind(i, j)), find(gind(i + 1, j)), a[i][j], a[i + 1][j]);
			}
			if (j != n && a[i][j] != a[i][j + 1]) {//建下方边 
				ed[++ecnt] = Edge(find(gind(i, j)), find(gind(i, j + 1)), a[i][j], a[i][j + 1]);
			}
		}
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) {
			int ind = gind(i, j);
			if (find(ind) == ind)//以颜色为节点,当前颜色连接着那些块 
				sts[a[i][j]].push_back(ind);
		}	
	sort(ed + 1, ed + ecnt + 1);
	memcpy(szcp, sz, sizeof(sz));
	for (int i = 1; i <= ecnt; ) {//处理边,也就是这些相邻块 
		int c1 = ed[i].c1, c2 = ed[i].c2;//排序后的相邻颜色已经在一起 
		while (ed[i].c1 == c1 && ed[i].c2 == c2) {//处理在一起的这些颜色 
			merge(ed[i].u, ed[i].v);
			a2 = max(a2, sz[find(ed[i].u)]);
			++i;
		}//处理完一种颜色匹配后,状态恢复,以备处理另一种颜色。 
		for (int j=0; j<sts[c1].size(); j++) {
			int u=sts[c1][j];
			f[u] = u;
			sz[u] = szcp[u];
		}
		for (int j=0; j<sts[c2].size(); j++) {
			int u=sts[c2][j];
			f[u] = u;
			sz[u] = szcp[u];
		}
	}
	printf("%d\n%d\n", a1, a2);
	return 0;
}

inline int gind(int x, int y) {
	return (x - 1) * n + y;
}

int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}

void merge(int x, int y) {
	x = find(x);
	y = find(y);
	if (x == y)
		return;
	sz[x] += sz[y];
	f[y] = x;
}

猜你喜欢

转载自blog.csdn.net/lengxuenong/article/details/80479873