2020杭电寒假专题训练1(搜索)

A Nightmare(hdu1072)

原题链接

Ignatius had a nightmare last night. He found himself in a labyrinth with a time bomb on him. The labyrinth has an exit, Ignatius should get out of the labyrinth before the bomb explodes. The initial exploding time of the bomb is set to 6 minutes. To prevent the bomb from exploding by shake, Ignatius had to move slowly, that is to move from one area to the nearest area(that is, if Ignatius stands on (x,y) now, he could only on (x+1,y), (x-1,y), (x,y+1), or (x,y-1) in the next minute) takes him 1 minute. Some area in the labyrinth contains a Bomb-Reset-Equipment. They could reset the exploding time to 6 minutes.
Given the layout of the labyrinth and Ignatius’ start position, please tell Ignatius whether he could get out of the labyrinth, if he could, output the minimum time that he has to use to find the exit of the labyrinth, else output -1.
Here are some rules:

  1. We can assume the labyrinth is a 2 array.
  2. Each minute, Ignatius could only get to one of the nearest area, and he should not walk out of the border, of course he could not walk on a wall, too.
  3. If Ignatius get to the exit when the exploding time turns to 0, he can’t get out of the labyrinth.
  4. If Ignatius get to the area which contains Bomb-Rest-Equipment when the exploding time turns to 0, he can’t use the equipment to reset the bomb.
  5. A Bomb-Reset-Equipment can be used as many times as you wish, if it is needed, Ignatius can get to any areas in the labyrinth as many times as you wish.
  6. The time to reset the exploding time can be ignore, in other words, if Ignatius get to an area which contain Bomb-Rest-Equipment, and the exploding time is larger than 0, the exploding time would be reset to 6.

BFS
注意一个点,因为可以往回走,为了避免陷入死循环,存入到每个点的最短时间。如果到这个点时,时间大于最短时间,则不走这个点。

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
int n,m;
int shijian[10][10];
int s[10][10];
struct node{
 int x,y,sum,time;
}st,en;
int beginx,beginy,endx,endy;
int dir[4][2]={1,0,-1,0,0,1,0,-1};
bool flag;
void bfs(){
 queue<node>q;
 st.x=beginx;
 st.y=beginy;
 st.sum=0;
 st.time=0;
 q.push(st);
 while(!q.empty()){
  st=q.front();
  q.pop();
  if(st.x==endx&&st.y==endy){
   flag=true;
   cout<<st.time<<endl;
   return;
  }
  for(int i=0;i<4;i++){
   en.x=st.x+dir[i][0];
   en.y=st.y+dir[i][1];
   en.sum=st.sum;
   en.time=st.time+1;
   if(en.x<1||en.y<1||en.x>n||en.y>m||s[en.x][en.y]==0||en.sum>=5)continue;
   if(s[en.x][en.y]==4){
    if(en.time>shijian[en.x][en.y])continue;
    else en.sum=0;
   }
   else en.sum=st.sum+1;
   shijian[en.x][en.y]=min(shijian[en.x][en.y],en.time);
   q.push(en);
  }
 }
}
int main(){
 int t;
 cin>>t;
 while(t--){
  flag=false;
  cin>>n>>m;
  for(int i=1;i<=n;i++){
   for(int j=1;j<=m;j++){
    shijian[i][j]=10000;
    cin>>s[i][j];
    if(s[i][j]==2){
     beginx=i;
     beginy=j;
    }
    else if(s[i][j]==3){
     endx=i;
     endy=j;
    }
   }
  }
  bfs();
  if(!flag)cout<<-1<<endl;
 } 
 return 0;
}

B 连连看(hdu1175)

原题链接

“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。

DFS
剪枝:当已经转了两次达到上限,但仍和终点不在同一行并不在同一列,则至少还要转一次,必不符合条件

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
int n,m,t;
int beginx,beginy,endx,endy;
int s[1005][1005];
bool vis[1005][1005];
bool flag;
int dir[4][2]={1,0,-1,0,0,1,0,-1};
void dfs(int x,int y,int zhuan,int sum){
 if(x==endx&&y==endy&&sum<=2){
  flag=true;
  return;
  }达到目标输出
  if(x<1||x>n||y<1||y>m)return;
 if(s[x][y]!=0&&(x!=beginx||y!=beginy))return; //除了终点只能走0
 if((x!=endx&&y!=endy)&&sum>=2)return; //剪枝点
 for(int i=0;i<4;i++){
  int tx=x+dir[i][0];
  int ty=y+dir[i][1];
  if(vis[tx][ty]==false){
   if((zhuan==-1||zhuan==0||zhuan==1)&&(i==0||i==1)){
    vis[tx][ty]=true;
    dfs(tx,ty,i,sum);
    vis[tx][ty]=false;
   } //无需转弯
   else if((zhuan==-1||zhuan==2||zhuan==3)&&(i==2||i==3)){
    vis[tx][ty]=true;
    dfs(tx,ty,i,sum);
    vis[tx][ty]=false;
   } //无需转弯
   else{
    vis[tx][ty]=true;
    dfs(tx,ty,i,sum+1);
    vis[tx][ty]=false;
   } //转弯
  }
 }
 
}
int main(){
 while(cin>>n>>m){
  if(n==0&&m==0)break;
  for(int i=1;i<=n;i++){
   for(int j=1;j<=m;j++){
    cin>>s[i][j];
   }
  }
  cin>>t;
  while(t--){
   flag=false;
   memset(vis,false,sizeof vis);
   cin>>beginx>>beginy>>endx>>endy;
   if(s[beginx][beginy]!=s[endx][endy]||s[endx][endy]==0){
    cout<<"NO"<<endl;
    continue;
   } //先要进行一个是否都相等以及都不为0的判断
   vis[beginx][beginy]=true;
   dfs(beginx,beginy,-1,0);
   if(flag)cout<<"YES"<<endl;
   else cout<<"NO"<<endl;
  }
 }
 return 0;
}
 }
 

G 非常可乐(hdu1495)

原题链接

大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

bfs
需要注意的是:此题目标为1个杯子为空,剩下两个杯子平分可乐

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
int mins;
int a[105][105][105];//用一个数组存储三个杯子不同状态所需最少时间
int b[3];//三个杯子
struct node{
 int q[3],sum;
}st,en;
int check(int x,int y,int z){
  if(x==0&&y==z)
        return 1;
    if(y==0&&x==z)
        return 1;
    if(z==0&&x==y)
        return 1;
 return 0;
}//判断条件是个坑点
void bfs(){
 queue<node>q;
 st.q[0]=b[0];
 st.q[1]=0;
 st.q[2]=0;
 st.sum=0;
 q.push(st);
 while(!q.empty()){
  st=q.front();
  q.pop();
  if(check(st.q[0],st.q[1],st.q[2])==1){
   cout<<st.sum<<endl;
   return;
  }
  //已经凑足两杯一半水量,直接输出 
  for(int i=0;i<=2;i++){//选择从哪个杯中倒水 
   for(int j=0;j<=2;j++){//选择接受杯 
    if(j==i)continue;//自己不倒自己 
    if(st.q[i]>=b[j]-st.q[j]){//第一种水量充足 倒满接受杯 
     en.q[0]=st.q[0];
     en.q[1]=st.q[1];
     en.q[2]=st.q[2];
     en.q[i]-=(b[j]-st.q[j]);
     en.q[j]=b[j];
     en.sum=st.sum+1;
     if(a[en.q[0]][en.q[1]][en.q[2]]==0||en.sum<=a[en.q[0]][en.q[1]][en.q[2]]){
      a[en.q[0]][en.q[1]][en.q[2]]=en.sum;
      q.push(en);
     }//储存达到对应三水量时的最小步 
    }
    else{//第二种 水量不足 将杯中所有水量倒给接受杯 
     en.q[0]=st.q[0];
     en.q[1]=st.q[1];
     en.q[2]=st.q[2];
     en.q[j]=st.q[j]+st.q[i];
     en.q[i]=0; 
     en.sum=st.sum+1;
     if(a[en.q[0]][en.q[1]][en.q[2]]==0||en.sum<=a[en.q[0]][en.q[1]][en.q[2]]){
      a[en.q[0]][en.q[1]][en.q[2]]=en.sum;
      q.push(en);
     }//储存达到对应三水量时的最小步
    }
   }
  }
 }
 cout<<"NO"<<endl;
}
int main(){
 while(cin>>b[0]>>b[1]>>b[2]){
  if(b[0]==0&&b[1]==0&&b[2]==0)break;
  if(b[0]%2==1){//水量奇数直接NO 
   cout<<"NO"<<endl;
   continue;
  }
  memset(a,0,sizeof a);
  a[b[0]][0][0]=-1;
  bfs();
 }
 return 0;
}

H 逃离迷宫

解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值