ZOJ - 1654 Place the Robots (构图 最大匹配)

题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=654

题意:

‘o’, ‘*’, ‘#’-> 空地, 草地, 墙壁

只有空地可以放机器人,且机器人会向四个方向开炮; 草地可以被炮穿过;墙壁能阻挡炮弹

求机器人不会相互攻击下的,最大机器人数目

分析:

刚开始,考虑到每个空地作为一个点,可以相互攻击的点,连上线,变成无向图后,问题就装换成了:从所有点中选出尽可能多的点,且选出的点不能相连 —— 最大独立点集

思路是清晰了,不过对于实现出了问题,没有有效的算法能求解最大独立点集,又因为:最大独立点集 = 总节点数 - 最大匹配

所以接下来开始构造一个关于最大匹配(边的最大独立数)的模型即可,由于是二维的地图,一般把行和列分开为x集合、y集合的二部图处理

最后用匈牙利算法求出二部图最大匹配得解

关于二部图的构建,例如:

5*5 mapt

o * * * #

* # # # *

o o # o o

* * * # o

# o * * o

首先,先把连续的一行(炮弹可以直接穿过点)进行分块,标记1~n

5*5 mapx

1 0 0 0 0

0 0 0 0 0

2 2 0 3 3

0 0 0 0 4

0 5 0 0 5

然后,把连续的一列也进行分块,标记为1~m

5*5 maxpy

1 0 0 0 0

0 0 0 0 0

1 2 0 3 4

0 0 0 0 4

0 2 0 0 4

最后,把分块之后的矩阵,对比,找出行列分块后的公共的空地,相连起来

n*m Edge[u][v](邻接矩阵:表示水平u行,竖直v列,有公共空地)

1 0 0 0

1 1 0 0

0 0 1 1

0 0 0 1

0 1 0 1

每条边,表示一块空地(一块横向+一块纵向),冲突的空地,则必有公共顶点

所有问题就转换成了:在二部图中,找没有公共顶点的最大边集 —— 最大匹配问题

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>

using namespace std;

#define MAX 51
#define MIN -1e9
#define INF 0x7f7f7f7f

int t, n, m; // 组数, 行列数

int x[MAX*MAX], y[MAX*MAX]; // 行列节点匹配情况
int Edge[MAX*MAX][MAX*MAX]; // x、y集合关联矩阵
int vis[MAX*MAX]; // path函数DFS时的防死循环数组

int Case; // 组数

int path(int u) // 查找增广路径
{
	for(int v = 1; v<=m; v++) // 每次xu, 都遍历所有y
	{
		if(vis[v] == 0 && Edge[u][v]) // 该次dfs未访问过的y && xu -> yv
		{
			vis[v] = 1;
			if(y[v] == -1 || path(y[v])) // yv 未被匹配 || 已经匹配,但是匹配yv的x可以更新匹配
			{
				x[u] = v;
				y[v] = u;
				return 1;
			}
		}
	}
	return 0;
}


void solve()
{
	memset(x, -1, sizeof(x)); // 初始x、y均未匹配
	memset(y, -1, sizeof(y));
	int ans = 0;
	for(int u = 1; u<=n; u++) // 遍历所有x
	{
		if(x[u] == -1)
		{
			memset(vis, 0, sizeof(vis));
			ans += path(u); // 增广路成功 -- 1 失败 -- 0
		}
	}
	printf("Case :%d\n", Case ++);
	printf("%d\n", ans);
}

int main()
{
	int i, j;
	//freopen("a.txt", "r", stdin);

	scanf("%d", &t);
	Case = 1;
	while(t--)
	{
		int nt, mt;
		char mapt[MAX][MAX]; // 存放字符数组
		int mapx[MAX][MAX], mapy[MAX][MAX]; // 存放分块后的值(行分块、列分块)
		
		memset(mapx, 0, sizeof(mapx));
		memset(mapy, 0, sizeof(mapy));
		
		scanf("%d%d", &nt, &mt);
		for(i = 0; i<nt; i++)
		{
			scanf("%s", mapt[i]);
		}
		
		int flag; // 分块标记
		
		n = 0; // 行分块的个数为 n(x集合元素个数)
		for(i = 0; i<nt; i++) // 行分块		
		{
			flag = 0; // 换行,更新分块标记
			for(j = 0; j<mt; j++)
			{
				if(mapt[i][j] == 'o')
				{
					if(flag == 0) n++; // 分块标记为0,则块数 +1
					mapx[i][j] = n; flag = 1; // 设置分块标记为1,知道需要更新分块
				}
				else if(mapt[i][j] == '#') flag = 0; // 遇到墙,更新分块标记
			}
		}
		
		m = 0; // 列分块的个数  m(y集合元素个数)
		for(j = 0; j<mt; j++) // 列分块
		{
			flag = 0;
			for(i = 0; i<nt; i++)
			{
				if(mapt[i][j] == 'o')
				{
					if(flag == 0) m++;
					mapy[i][j] = m; flag = 1;
				}
				else if(mapt[i][j] == '#') flag = 0;
			}
		}
		
		memset(Edge, 0, sizeof(Edge));
		for(i = 0; i<nt; i++)  // 查找公共空地
		{
			for(j = 0; j<mt; j++)
			{
				if(mapx[i][j] * mapy[i][j] != 0) // 行块 -> 列块 重合
				{
					Edge[mapx[i][j]][mapy[i][j]] = 1;
				}
			}
		}
		solve(); // 匈牙利 -> 最大匹配数
	}
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值