HDU 3468 — Treasure Hunting BFS+二分匹配

原题:http://acm.hdu.edu.cn/showproblem.php?pid=3468

题意:

给定一个n*m的地图;

.表示空地,#表示墙,*表示黄金;

从A走到Z或者从a走到z;

要求每次都按最短路走到下一个字母,且走的字母是连续的;

若走不到下一个字母或者地图上不存在下一个字母就输出-1;

每次走到下一个字母的途中可以拿一个黄金,问最多可以得到多少个黄金;


思路:

尽量从每个字母走出来都能取一个黄金,二分匹配;

把起点字母作为X集,黄金作为Y集,映射为黄金在起点到终点的最短路上;

而黄金在最短路上所要满足的条件为:起点到终点的距离 = 起点到黄金的距离+终点到黄金的距离;


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

const int N = 110;
using namespace std;
int n, m;
int num, k;
int dis[N][N*N], poi[N*N], r[N], gold[N*N];		//r表示字母在poi中的编号; 
char s[N][N];
bool vis[N*N], used[N*N];
int match[N*N];
int f[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};

vector<int>G[N];
void bfs(int u)
{
	memset(vis, false, sizeof(vis));
	memset(dis[u], -1, sizeof(dis[u]));
	queue<int>q;
	vis[poi[u]] = true;
	dis[u][poi[u]] = 0;
	q.push(poi[u]);
	while(!q.empty())
	{
		int l = q.front();
		q.pop();
		int x = l/m;
		int y = l%m;
		for(int i = 0;i<4;i++)
		{
			int a = x+f[i][0];
			int b = y+f[i][1];
			int now = a*m+b;
			if(s[a][b] == '#')	continue;
			if(a<0 || b<0 || a>=n || b>=m)	continue;
			if(vis[now])	continue;
			vis[now] = true;
			dis[u][now] = dis[u][l]+1;
			q.push(now);
		}
	}
}

bool solve(int x)
{
	for(int i = 0;i<G[x].size();i++)
	{
		int v = G[x][i];
		if(!used[v])
		{
			used[v] = true;
			if(match[v] == -1||solve(match[v]))
			{
				match[v] = x;
				return true;
			}
		}
	}
	return false;
}

int find(char c)
{
	if(c>='A' && c<='Z')
	return c-'A';
	if(c>='a' && c<='z')
	return c-'a'+26;
}

int main()
{
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		k = num = 0;
		memset(r, -1, sizeof(r));
		for(int i = 0;i<n;i++)
		{
			scanf("%s", s[i]);
			for(int j = 0;j<m;j++)
			{
				if(isalpha(s[i][j]))
				{
					poi[k] = i*m+j;
					r[find(s[i][j])] = k;
					k++;
				}
				if(s[i][j] == '*')
					gold[num++] = i*m+j;
			}
		}
		for(int i = 0;i<k;i++)
		{
			bfs(i);
			G[i].clear();
		}
		int i;
		for(i = 0;i<k-1;i++)
		{
			if(r[i] == -1 || r[i+1] == -1)
			break;
			if(dis[r[i]][poi[r[i+1]]] == -1)
			break;
		}
		if(i!=k-1)
		{
			printf("-1\n");
			continue;
		}
		for(int i = 0;i<k-1;i++)
		{
			for(int j = 0;j<num;j++)
			{
				if(dis[r[i]][gold[j]] == -1 || dis[r[i+1]][gold[j]] == -1)
				continue;
				if(dis[r[i]][gold[j]]+dis[r[i+1]][gold[j]] == dis[r[i]][poi[r[i+1]]])
				G[i].push_back(j);
			}
		}
		memset(match, -1, sizeof(match));
		int sum = 0;
		for(int i = 0;i<k-1;i++)
		{
			memset(used, false, sizeof(used));
			if(solve(i))
			sum++;
		}
		printf("%d\n", sum);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值