HDU 3468 Treasure Hunting (最短路+二分图匹配)

Sample Input
   
   
   
   
2 4 A.B. ***C 2 4 A#B. ***C
 

Sample Output
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;
}

 

你可能感兴趣的:(HDU 3468 Treasure Hunting (最短路+二分图匹配))