HDU 3468 BFS+二分匹配

九野的博客,转载请注明出处 http://blog.csdn.net/acmmmm/article/details/10966383

开始建图打搓了,参考了大牛的题解打的版本比较清爽,后来改的基本雷同了http://www.cnblogs.com/woaishizhan/archive/2013/04/08/3008719.html

题意:给定n,m表示下面地图大小

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

行走的路线是A->Z->a->z

规则,必须从字母依次走最短路到下一个字母(字母必须连续走,如果走不到下一个字母或者地图上不存在下一个字母则输出-1)

每次走到下一个字母可以取走路途上的一个黄金,问最多能取走几个黄金

 

思路:走到最后一个字母就结束了,所以希望从每个字母走出来都能得到一个黄金,所以是二分匹配

把起点字母作为X集, 黄金作为Y集, 映射条件是黄金在 字母间行走的最短路上

然后这里用BFS寻找路径建图

最后套个二分匹配的模版

 

#include<stdio.h>

#include<algorithm>

#include<iostream>

#include<set>

#include<math.h>

#include<string.h>

#include<queue>

#include<vector>

#define N 105

#define inf 999999999

using namespace std;

int n,M;

int road[N],p[N*N],gold[N*N],num,pn;//road 表示字母在p中的编号,p是字母的坐标(一维化)

int dis[N][N*N];//dis[a][b] 表示离散化的字母点a到 一维化的坐标b的距离

char map[N][N];

vector<int>G[N];



int go[4][2]={1,0,0,1,-1,0,0,-1};

void bfs(int s){

	bool vis[N*N];

	memset(vis,0,sizeof(vis));

	queue<int>q;



	memset(dis[s],-1,sizeof(dis[s]));

	dis[s][p[s]]=0;



	q.push(p[s]);

	vis[p[s]]=1;

	while(!q.empty())

	{

		int t=q.front();

		int x=t/M,y=t%M;

		q.pop() ;

		for(int i=0;i<4;i++)

		{

			int nowx=x+go[i][0],nowy=y+go[i][1];

			int now=nowx*M+nowy;

			if(map[nowx][nowy]=='#')continue;

			if(nowx<0 || nowx>=n ||nowy<0||nowy>=M)continue;

			if(vis[now])continue;

			vis[now]=1;



			dis[s][now]=dis[s][t]+1;

			q.push(now);

		}

	}

}

int lef[N*N];//lef[v]表示右边点v 当前连接的点

bool T[N*N];//右边的点是否连过



bool match(int x)

{

	for(int i=0;i<G[x].size();i++)

	{

		int v=G[x][i];

		if(!T[v])

		{

			T[v]=true;

			if(lef[v]==-1||match(lef[v]))

			{

				lef[v]=x;

				return true;

			}

		}

	}

	return false;

}



void solve()

{

	int ans=0;

	memset(lef,-1,sizeof(lef));

	for(int i=0;i<pn-1;i++)//左右点集匹配,左点集是 0-(pn-1) 右点集是G[左点].size()

	{

		memset(T,0,sizeof(T));



		if(match(i))ans++;

	}

	printf("%d\n",ans);

}



int f(char c){

	if('A'<=c && c<='Z')return c-'A';

	else if('a'<=c && c<='z')return c-'a'+26;

}

int main(){

	int i,j;

	while(~scanf("%d%d",&n,&M)){

		pn=num=0;

		memset(road,-1,sizeof(road));//这句没有最后第二个案例过不了

		for(i=0;i<n;i++)

		{

			scanf("%s",map[i]);

			for(j=0;j<M;j++)

				if(isalpha(map[i][j]))

				{

					p[pn]=i*M+j;

					road[f(map[i][j])]=pn;

					pn++;

				}

				else if(map[i][j]=='*')

				{

					gold[num++]=i*M+j;

	 			}

		}

		

		for(i=0;i<pn;i++)bfs(i),G[i].clear();

		for(i=0;i<pn-1;i++)

		{

			if(road[i]==-1||road[i+1]==-1)	        break;

			if(dis[road[i]][p[road[i+1]]]==-1) 	break;

		}

		if(i!=pn-1){puts("-1");continue;}

		for(i=0;i<pn-1;i++)

			for(j=0;j<num;j++)

			{

				if(dis[road[i]][gold[j]]==-1 || dis[road[i+1]][gold[j]]==-1)continue;//j这点的黄金到不了字母点

				if(dis[road[i]][gold[j]]+dis[road[i+1]][gold[j]]==dis[road[i]][p[road[i+1]]])

					G[i].push_back(j);

			}

		solve();

	}

	return 0;

}

/*

3 4

A#B.

***C

.D..



4 4

A#B.

***C

.D..

..E*



4 4

A#B.

***C

.D..

.*E*



4 4

A#B.

***C

.D#.

.#E*



4 4

A#B.

***C

.D#.

.#E.



4 4

A#B.

***C

.D##

.#E.



4 4

A#B.

***C

.D#*

.#E.



4 4

a#b.

***c

.d#*

.#e.



4 4

A#B*

*.*C

..D#

E*..





ans;

3

3

4

4

3

-1

4

-1

4



*/


 

 

 

 

你可能感兴趣的:(HDU)