ch6091 骑士放置(二分图最大独立集)

题目链接 https://www.acwing.com/problem/content/380/

思路:

让求最多可以放多少个互相之间不攻击的骑士。可以这样考虑,将图中可以互相攻击的点连边,会得到一个图,而且是一个二分图。如果分不清哪些是左部节点哪些是右部节点,可以采用染色法对其进行分别。现在要求的是一个最大的集合满足集合中任意两点之间没有连线,即最大独立集。最大独立集 = 二分图中的点的总数 - 最大匹配。本题中,二分图中的点的总数也可以用染色法中被染色的点数表示,更简单的表示就是,一共n*m个点,其中又t个点被禁止放置了,那么这t个点就不会出现在二分图中,所以总点数是n*m-t,然后建图求最大匹配即可。

/**
 * Author: correct
 */
#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof a)
const int N = 1e5;
int head[N], nex[N], to[N], cnt;
int n, m, t;
int color[110][110];
int dx[] = {2, 2, -2, -2, 1, 1, -1, -1};
int dy[] = {1, -1, 1, -1, 2, -2, 2, -2};
int match[N];
bool w[110][110];
bool vis[N];
void pre_work(){
	mem(head, -1);
	mem(nex, -1);
	cnt = 0;
	mem(color, 0);
	mem(match, -1);
	mem(w, true);
}
void add(int a, int b){
	++cnt;
	to[cnt] = b;
	nex[cnt] = head[a];
	head[a] = cnt;
}
int get(int x, int y){
	return (x - 1) * 1000 + y;
}
bool check(int xx, int yy){
	if (xx >= 1 && xx <= n && yy >= 1 && yy <= m && w[xx][yy])return 1;
	return 0;
}
void build(){
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= m; j++){
			for (int k = 0; k <= 7; k++){
				int xx = i + dx[k];
				int yy = j + dy[k];
				if (check(xx, yy)){
					add(get(i, j), get(xx, yy));
				}
			}
		}
	}
}
int getX(int x){
	return x / 1000 + 1;
}
int getY(int x){
	return x % 1000;
}
bool colors(int x, int c){
	int xx, yy;
	yy = x % 1000;
	xx = x / 1000 + 1;
	color[xx][yy] = c;
	for (int i = head[x]; ~i; i = nex[i]){
		int y = to[i];
		if (!color[getX(y)][getY(y)]){
			colors(y, 3 - c);
		}
		else if(color[getX(y)][getY(y)] == c){
			return 0;
		}
	}
	return 1;
}
bool dfs(int x){
	for (int i = head[x]; ~i; i = nex[i]){
		int y = to[i];
		if (vis[y])continue;
		vis[y] = 1;
		if (match[y] == -1 || dfs(match[y])){
			match[y] = x;
			return 1;
		}
	}
	return 0;
}
int solve(){
	build();
	int ans = n * m - t;
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= m; j++){
			if (!color[i][j])colors(get(i, j), 1);
		}
	}
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= m; j++){
			if (color[i][j] == 1 && w[i][j]){
				mem(vis, 0);
				if (dfs(get(i, j)))ans--;
			}
		}
	}
	return ans;
}
int main()
{
	pre_work();
	scanf("%d %d %d", &n, &m, &t);
	for (int i = 0; i < t; i++){
		int x, y;
		scanf("%d %d", &x, &y);
		w[x][y] = 0;
	}
	printf("%d\n", solve());
	return 0;
}

 

posted @ 2020-03-04 21:12  correct  阅读(132)  评论(0)    收藏  举报