2 4 A.B. ***C 2 4 A#B. ***C
1 2
题意:给定n,m表示下面地图大小
.表示空地 #表示墙 *表示黄金
行走的路线是A->Z->a->z (行走路线,必须是连续的字母)
规则,必须从字母依次走最短路到下一个字母(字母必须连续走,如果走不到下一个字母或者地图上不存在下一个字母则输出-1)
每次走到下一个字母可以取走路途上的一个黄金,问最多能取走几个黄金 。
思路:走到最后一个字母就结束了,所以希望从每个字母走出来都能得到一个黄金,所以是二分匹配
把起点字母作为X集, 黄金作为Y集, 映射条件是黄金在 字母间行走的最短路上
然后这里用BFS寻找路径建图
最后套个二分匹配的模版
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 110 ;
const int inf=99999999 ;
int n,m ;
int road[N],p[N*N],gold[N*N],num,pn;
int dis[N][N*N];
char mm[N][N];
int match[N*N],vist[N*N],vis[N*N];
vector<int>G[N];
int g[4][2]={1,0,0,1,-1,0,0,-1};
void bfs(int s)
{
memset(vis,0,sizeof(vis));
memset(dis[s],-1,sizeof(dis[s]));
queue<int>q;
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+g[i][0],nowy=y+g[i][1];
int now=nowx*m+nowy;
if(mm[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 dfs(int u)
{
for(int i = 0 ; i < G[u].size();i++)
{
int v=G[u][i] ;
if(!vist[v])
{
vist[v]=1;
if(match[v]==-1 || dfs(match[v]))
{
match[v]=u;
return 1;
}
}
}
return 0;
}
int f(char c)
{
if('A'<=c && c <='Z') return c-'A';
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",mm[i]);
for( j = 0 ; j < m; j++)
if(isalpha(mm[i][j])) //若该点事字母
{
p[pn]=i*m+j; //坐标一维化,表示该字母的坐标
road[f(mm[i][j])]=pn; //road[a],是第a个字母在p数组的下标。
pn++ ; //即p[road[a]]表示第a个字母的坐标;
}
else if(mm[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 ; //如果输出的字母不是以A开头,并且连续,则退出
if(dis[road[i]][p[road[i+1]]] == -1) break ;//如i字母与下一个字母没有边连。。
}
if(i!=pn-1) {puts("-1");continue;} ; //严格符合连续的字母;
for( i = 0 ; i < pn-1 ; i++)
for( j = 0 ; j < num ; j++)
{
//黄金j不在i字母和j字母间 ;
if(dis[road[i]][gold[j]]==-1 || dis[road[i+1]][gold[j]]==-1) continue ;
//i->j有黄金,还要判断黄金j是否在ij的最短路上;
if(dis[road[i]][gold[j]]+dis[road[i+1]][gold[j]] == dis[road[i]][p[road[i+1]]])
G[i].push_back(j);
}
int ans = 0 ;
memset(match,-1,sizeof(match));
for( i = 0 ; i < pn-1 ; i++)
{
memset(vist,0,sizeof(vist));
ans += dfs(i) ;
}
cout << ans << endl ;
}
return 0;
}