紫书章七例题九 The Moring agter Halloween UVA1601(bfs,重新建图)

题意:给你一个图,然后小写字母同时去找大写字母。然后可以不动,上下左右移动(5种移动方式)。其中小写字母不能占用同一个位子,也不能在一步之内交换位子。
强行暴力会超时,但是,你强行暴力的时候,标记数组怎么标记?
所以这里采用的是将每一个可行的位子记录下来,建一个新图。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
int nx[]={0,0,0,1,-1};
int ny[]={0,1,-1,0,0};//不动下上右左
int s[5],t[5];
int G[205][10],dg[205];
int d[200][200][200];
struct node
{
  int a,b,c;
};
int judge(int a,int b,int aa,int bb)
{
  if(aa==bb||(aa==b&&bb==a))
    return 1;
  else return 0;
}
void bfs()
{
  memset(d,-1,sizeof(d));
  queue<node> q;
  q.push((node){s[0],s[1],s[2]});
  d[s[0]][s[1]][s[2]]=0;
  while(!q.empty())
  {
    node t1=q.front();
    q.pop();
    if(t1.a==t[0]&&t1.b==t[1]&&t1.c==t[2])
    {
      printf("%d\n",d[t1.a][t1.b][t1.c]);
      return ;
    }
    for(int i=0;i<dg[t1.a];i++){
        int aa=G[t1.a][i];
      for(int j=0;j<dg[t1.b];j++){
        int bb=G[t1.b][j];
        if(judge(t1.a,t1.b,aa,bb)) continue;
        for(int k=0;k<dg[t1.c];k++){
          int cc=G[t1.c][k];
          if(judge(t1.a,t1.c,aa,cc)) continue;
          if(judge(t1.b,t1.c,bb,cc)) continue;
          if(d[aa][bb][cc]!=-1) continue;
          d[aa][bb][cc]=d[t1.a][t1.b][t1.c]+1;
          q.push((node){aa,bb,cc});
        }
      }
    }
  }
}
int main()
{
  int w,h,n;
  while(scanf("%d %d %d",&w,&h,&n)!=EOF&&n){
  char M[20][20];
  int x[400],y[400];
  int cnt=0,id[20][20];
  memset(dg,0,sizeof(dg));
  memset(G,0,sizeof(G));
  memset(id,0,sizeof(id));
  getchar();
  for(int i=0;i<h;i++)
  {
    fgets(M[i],20,stdin);
    for(int j=0;j<w;j++){
      if(M[i][j]!='#')
      {
        y[cnt]=i,x[cnt]=j,id[i][j]=cnt;//id记录图中可行位子的编号,以便后面的新图
        if(islower(M[i][j])) s[M[i][j]-'a']=cnt;
        else if(isupper(M[i][j])) t[M[i][j]-'A']=cnt;
        cnt++;
      }
    }
  }
  for(int i=0;i<cnt;i++)
  {
    for(int j=0;j<5;j++){
      int xx=x[i]+nx[j];
      int yy=y[i]+ny[j];
      if(M[yy][xx]!='#'){
        G[i][dg[i]++]=id[yy][xx];//存图
      }
    }
  }
  //如果没有三个,就填充为3个,方便同一处理
  if(n<=1)
  {
    s[1]=cnt,t[1]=cnt,G[cnt][0]=cnt,dg[cnt]=1;
    cnt++;
  }
  if(n<=2){
    s[2]=cnt,t[2]=cnt,G[cnt][0]=cnt,dg[cnt]=1;
    cnt++;
  }
  bfs();
  }
  return 0;
}

双向bfs,还是基于第一种方法。然后建好新图之后,因为我们知道起点和终点。然后我们就可以起点和终点同时走,然后相遇了,时间之和即为所求。相当于正着走一步,反走再走一步。然后一定要注意那个pop,要在之后用,因为有可能为下一步了,还没判断,就出去了。。。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
int nx[]={0,0,0,1,-1};
int ny[]={0,1,-1,0,0};//不动下上右左
int s[5],t[5];
int G[205][10],dg[205];
int d1[200][200][200];
int d2[200][200][200];

struct node
{
  int a,b,c;
};
queue <node> q1;
queue <node> q2;
int judge(int a,int b,int aa,int bb)
{
  if(aa==bb||(aa==b&&bb==a))
    return 1;
  else return 0;
}
int bfs1()
{
  node u=q1.front();
  int ans=d1[u.a][u.b][u.c];
  while(!q1.empty())
  {
    node t1=q1.front();
    if(d1[t1.a][t1.b][t1.c]!=ans) return 0;
    if(d2[t1.a][t1.b][t1.c]!=-1) return 1;
    for(int i=0;i<dg[t1.a];i++){
        int aa=G[t1.a][i];
      for(int j=0;j<dg[t1.b];j++){
        int bb=G[t1.b][j];
        if(judge(t1.a,t1.b,aa,bb)) continue;
        for(int k=0;k<dg[t1.c];k++){
          int cc=G[t1.c][k];
          if(judge(t1.a,t1.c,aa,cc)) continue;
          if(judge(t1.b,t1.c,bb,cc)) continue;
          if(d1[aa][bb][cc]==-1){
          d1[aa][bb][cc]=d1[t1.a][t1.b][t1.c]+1;
          q1.push((node){aa,bb,cc});
          }
        }
      }
    }
    q1.pop();
  }
  return 0;
}
int bfs2()
{
  node u=q2.front();
  int ans=d2[u.a][u.b][u.c];
  while(!q2.empty())
  {
    node t1=q2.front();
    if(ans!=d2[t1.a][t1.b][t1.c]) return 0;
    if(d1[t1.a][t1.b][t1.c]!=-1) return 1;
    for(int i=0;i<dg[t1.a];i++){
        int aa=G[t1.a][i];
      for(int j=0;j<dg[t1.b];j++){
        int bb=G[t1.b][j];
        if(judge(t1.a,t1.b,aa,bb)) continue;
        for(int k=0;k<dg[t1.c];k++){
          int cc=G[t1.c][k];
          if(judge(t1.a,t1.c,aa,cc)) continue;
          if(judge(t1.b,t1.c,bb,cc)) continue;
          if(d2[aa][bb][cc]==-1) {
          d2[aa][bb][cc]=d2[t1.a][t1.b][t1.c]+1;
          q2.push((node){aa,bb,cc});
          }
        }
      }
    }
    q2.pop();//注意这个是在后面
  }
  return 0;
}
int BFS()
{
  while(!q1.empty())
    q1.pop();
  while(!q2.empty())
    q2.pop();
  q1.push((node){s[0],s[1],s[2]});
  q2.push((node){t[0],t[1],t[2]});
  int ok=0,step=0;
  memset(d1,-1,sizeof(d1));
  memset(d2,-1,sizeof(d2));
  d1[s[0]][s[1]][s[2]]=0;
  d2[t[0]][t[1]][t[2]]=0;
  while(!q1.empty()&&!q2.empty())
  {
    if(bfs1())
    {
      ok=1;
      break;
    }
    else
      step++;
    if(bfs2())
    {
      ok=1;
      break;
    }
    else step++;
  }
  return step;
}

int main()
{
  int w,h,n;
  while(scanf("%d %d %d",&w,&h,&n)!=EOF&&n){
  char M[20][20];
  int x[400],y[400];
  int cnt=0,id[20][20];
  memset(dg,0,sizeof(dg));
  memset(G,0,sizeof(G));
  memset(id,0,sizeof(id));
  memset(t,0,sizeof(t));
  memset(s,0,sizeof(s));
  getchar();
  for(int i=0;i<h;i++)
  {
    fgets(M[i],20,stdin);
    for(int j=0;j<w;j++){
      if(M[i][j]!='#')
      {
        y[cnt]=i,x[cnt]=j,id[i][j]=cnt;//id记录图中可行位子的编号,以便后面的新图
        if(islower(M[i][j])) s[M[i][j]-'a']=cnt;
        else if(isupper(M[i][j])) t[M[i][j]-'A']=cnt;
        cnt++;
      }
    }
  }
  for(int i=0;i<cnt;i++)
  {
    for(int j=0;j<5;j++){
      int xx=x[i]+nx[j];
      int yy=y[i]+ny[j];
      if(M[yy][xx]!='#'){
        G[i][dg[i]++]=id[yy][xx];//存图
      }
    }
  }
  //如果没有三个,就填充为3个,方便同一处理
  if(n<=1)
  {
    s[1]=cnt,t[1]=cnt,G[cnt][0]=cnt,dg[cnt]=1;
    cnt++;
  }
  if(n<=2){
    s[2]=cnt,t[2]=cnt,G[cnt][0]=cnt,dg[cnt]=1;
    cnt++;
  }
  printf("%d\n",BFS());
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值