HDU - 5556 Land of Farms 枚举 + 无向二分图最大独立集

题意:有一个农场大小为n*m  里面有一些古老的农田,你现在需要新建一些农田,要求新建的农田之间不能相连,古老的农田不可拆分,如果你选择了一块土地(原为古老的农田)建立新农田则该新农田需要把一整块连通的古老的农田全部包含。问最多能新建多少个农田。

思路:由于古老的农田只有10个,因此我们可以枚举选哪几个古老的农田,然后对于剩下的空地,相邻空地之间建边,求一个二分图的最大独立集就是剩下的空地中最多能新建的农田数,再加上选出来的古老农田数就行了。

注意选出来的古老农田四周的空地不能建图,以避免新农田之间相连。

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 110;
int N, M;
int mp[15][15];
int go[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
int uN, vN;
bool g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)
{
    for(int v = 1; v <= vN; v++)
    if(g[u][v] && !used[v])
    {
        used[v] = 1;
        if(linker[v] == -1 || dfs(linker[v]))
        {
            linker[v] = u;
            return 1;
        }
    }
    return 0;
}
int hungary()
{
    int res = 0;
    memset(linker, -1, sizeof(linker));
    for(int u = 1; u <= uN; u++)
    {
        memset(used, 0, sizeof used);
        if(dfs(u)) res++;
    }
    return res;
}
bool check(int status)
{
	int r, c;
	for(int i = 1; i <= N; i++)
	{
		for(int j = 1; j <= M; j++)
		{
			if(mp[i][j] == '.') continue;
			if(!(status >> mp[i][j] & 1)) continue;
			for(int k = 0; k < 4; k++)
			{
				r = i + go[k][0];
				c = j + go[k][1];
				if(r < 1 || c < 1 || r > N || c > M) continue;
				if(mp[r][c] == '.' || mp[r][c] == mp[i][j]) continue;
				if(status >> mp[r][c] & 1) return 1;
			}
		}
	}
	return 0;
}
bool judge(int i, int j, int status)
{
	int r, c;
	for(int k = 0; k < 4; k++)
	{
		r = i + go[k][0];
		c = j + go[k][1];
		if(r < 1 || c < 1 || r > N || c > M) continue;
		if(mp[r][c] == '.') continue;
		if(status >> mp[r][c] & 1) return 1;
	}
	return 0;
}
int solve(int status)
{
	int r, c, cnt = 0;
	uN = vN = N * M;
	memset(g, 0, sizeof(g));
	for(int i = 1; i <= N; i++)
	{
		for(int j = 1; j <= M; j++)
		{
			if(mp[i][j] != '.') continue;
			if(judge(i, j, status)) continue;
			cnt++;
			if((i + j) & 1) continue;
			for(int k = 0; k < 4; k++)
			{
				r = i + go[k][0];
				c = j + go[k][1];
				if(r < 1 || c < 1 || r > N || c > M) continue;
				if(mp[r][c] != '.' || judge(r, c, status)) continue;
				g[(i - 1) * M + j][(r - 1) * M + c] = 1;
			}
		}
	}
	return cnt - hungary();
}
int bk[300], tid;
int main()
{
	//cout << (char)9;
    int T, r, c, x, y;
    cin >> T;
    for(int kase = 1; kase <= T; kase++)
    {
    	tid = 0;
    	memset(bk, -1, sizeof(bk));
        scanf("%d %d", &N, &M);
        for(int i = 1; i <= N; i++)
        {
        	for(int j = 1; j <= M; j++)
        	{
        		scanf(" %c", &mp[i][j]);
        		if(mp[i][j] == '.') continue;
        		if(bk[mp[i][j]] == -1) bk[mp[i][j]] = tid++;
        		mp[i][j] = bk[mp[i][j]];
			}
		}
		int up = 1 << tid, ans = 0;
       	for(int i = 0; i < up; i++)
       	{
       		if(check(i)) continue;
       		ans = max(ans, solve(i) + __builtin_popcount(i));
		}
        printf("Case #%d: %d\n", kase, ans);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值