[luoguP3231] [HNOI2013]消毒(最小点覆盖 + 状压)

传送门

 

考虑贪心,控制某一维为1,另两位最大是最优的,也就是一次选一个厚度为1的面

那么对于每个点,可以有3种面是可以选到它的

然后gg

考虑二维的状态,一个平面,有些点,一次选一行或一列最优

那么每一个点i,j可以被行i和列j选中,将i->j连接一条边,每一条边就代表一个点

选取最少的点覆盖所有边就是最少点覆盖=最大匹配

因为a*b*c<=5000所以最小的那一维一定<=17,可以枚举这一维哪些面被一次清除,

#include <cstdio>
#include <cstring>
#include <iostream>
#define N 5001

using namespace std;

int T, a, b, c, n, ans, cnt;
int now[N][N], head[N], to[N * N], nex[N * N], belong[N];
bool vis[N];

struct node
{
	int x, y, z;
	node(int x = 0, int y = 0, int z = 0) : x(x), y(y), z(z) {}
}p[N];

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
	for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}

inline void clear()
{
	int i;
	ans = cnt = 0;
	for(i = 1; i <= b; i++) head[i] = -1;
	for(i = 1; i <= c; i++) belong[i] = 0;
}

inline void add(int x, int y)
{
	to[cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt++;
}

inline bool dfs(int u)
{
	int i, v;
	for(i = head[u]; ~i; i = nex[i])
	{
		v = to[i];
		if(!vis[v])
		{
			vis[v] = 1;
			if(!belong[v] || dfs(belong[v]))
			{
				belong[v] = u;
				return 1;
			}
		}
	}
	return 0;
}

inline int solve()
{
	int i, j, k, ret = 1e9;
	for(k = 0; k < (1 << a); k++)
	{
		clear();
		for(i = k; i; i >>= 1)
			if(i & 1) ans++;
		for(i = 1; i <= n; i++)
			if(!(k & (1 << p[i].x - 1))) now[p[i].y][p[i].z] = 1;
		for(i = 1; i <= b; i++)
			for(j = 1; j <= c; j++)
				if(now[i][j]) add(i, j);
		for(i = 1; i <= b; i++)
		{
			for(j = 1; j <= c; j++) vis[j] = 0;
			ans += dfs(i);
		}
		ret = min(ret, ans);
		for(i = 1; i <= n; i++)
			if(!(k & (1 << p[i].x - 1))) now[p[i].y][p[i].z] = 0;
	}
	return ret;
}

int main()
{
	int i, j, k, x;
	T = read();
	while(T--)
	{
		n = 0;
		a = read();
		b = read();
		c = read();
		for(i = 1; i <= a; i++)
			for(j = 1; j <= b; j++)
				for(k = 1; k <= c; k++)
				{
					x = read();
					if(x) p[++n] = node(i, j, k);
				}
		if(c < a && c < b)
		{
			swap(a, c);
			for(i = 1; i <= n; i++) swap(p[i].x, p[i].z);
		}
		else if(b < c && b < a)
		{
			swap(a, b);
			for(i = 1; i <= n; i++) swap(p[i].x, p[i].y);
		}
		printf("%d\n", solve());
	}
	return 0;
}

  

剩余的面压缩到一起,连边跑匈牙利

 

posted @ 2018-01-10 20:52  zht467  阅读(240)  评论(0编辑  收藏  举报