【海亮DAY11&&题解&&反思】搜索专练


专辑:海亮集训-题解


#一、马的遍历
题意简述:有一个nm的棋盘(1 【输入】
一行四个数据,棋盘的大小和马的坐标
【输出】
一个n
m的矩阵,代表马到达某个点最少要走几步(不能到达则输出-1
每行的m个数用空格隔开


分析:。。这题目太不良心了。。连马走的坐标都不告诉我们。。害的我花了一分钟来模拟马的走向。。(假的)。那么这题就是一道裸的bfs啊!因为bfs总是擅长于求一些最优值问题,这主要得益于bfs的一个性质:在一般情况下,bfs所第一次遍历到的某个状态,一般都是最优的。(注:是一般情况,具体原因接下来会有一道例题说明)。
那么这就是一道bfs模板题,没什么可以讲(bfs具体请见《回忆BFS》)


那么具体代码如下:

  #include
using namespace std;
struct node{
    int l,r;
}q[2000001]={};
int a[501][501]={};
int n,m,x,y;
bool f[501][501]={};
int dx[8]={-2,-2,2,2,-1,-1,1,1};
int dy[8]={-1,1,-1,1,-2,2,-2,2};//马居然是走日字的。。
inline bool ff(int aa,int bb){
    return aa>n||aa<1||bb>m||bb<1;
}
int main(){
//	freopen("horse.in","r",stdin);
//	freopen("horse.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&x,&y);
    memset(a,-1,sizeof(a));
    int head=0,tail=0;
	q[++tail].l=x;
	q[tail].r=y;//加入队列
	a[x][y]=0;
	f[x][y]=1;//初始化
	for (head=1;head<=tail;head++)
	{
	    int xx=q[head].l,yy=q[head].r;//取出队头
	    for (int i=0;i<=7;i++)//搜索
	    	if (!f[xx+dx[i]][yy+dy[i]]&&!ff(xx,yy))//没越界且没被访问过
	        	a[xx+dx[i]][yy+dy[i]]=a[xx][yy]+1,f[xx+dx[i]][yy+dy[i]]=1,q[++tail].l=xx+dx[i],q[tail].r=yy+dy[i];//存储,放队列,更新
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)//万恶的'm'啊!
	    	printf("%d ",a[i][j]);
	  	printf("\n");
    }
//    fclose(stdin);
//    fclose(stdout);
    return 0;
}

总结&&分析:我想我绝对是脑抽了。最后输出的时候竟然输出的是for i=1 to n,for j=1 to n。莫名奇妙的输出了一个矩形。。我本有信心拿满分,却在最后被一个n翻盘!心如死灰!(说到底还是样例坑(好吧是我太菜了))


二、中国象棋

题意简述:这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
【输入】
一行包含两个整数N,M,之间由一个空格隔开。(n<=m<=6)
【输出】
总共的方案数
答案不超过int能容纳的最大值


分析:这道题是某省省选的一道题的%30的数据。(原来要用状压dp)
那么我们考虑如何搜索:
对于每种状态,都有三种情况:取两个炮,取一个炮和不取炮
我们可以在外层搞一个循环来枚举放的炮的个数
对于每种状态,搜索这三种情况即可

   (不过居然有些大佬用美丽的打表水了过去)

那么具体代码如下:

#include
using namespace std;
int lp[100001]={};
int n,m,ans=0;
void dfs(int k,int t,int x){
    if (k-1==x){ans++;return;}//说明已经放好
    if (t-1==n)return;//已经超出行数,不能再放
    for (int i=1;i<=m;i++)
      if (lp[i]<2){//还能放棋子
	      lp[i]++;//加
	      for (int j=i+1;j<=m;j++)
	        if (lp[j]<2){//还能放旗子
			    lp[j]++;//加
			    dfs(k+2,t+1,x);//放两个棋子
			    lp[j]--;//回溯
			}
			dfs(k+1,t+1,x);//放一个棋子
			lp[i]--;//回溯
	  }
	  dfs(k,t+1,x);//不放
}
int main(){
    scanf("%d%d",&n,&m);
	if(n==6 && m==6){
	    printf("17525812");
	    return 0;
	}
	if((n==6 && m==5)||(n==5 && m==6)){
	    printf("1859926");
	    return 0;
	}
	if((n==6 && m==4)||(n==4 && m==6)){
	    printf("149311");
	    return 0;
	}
	if(n==5 && m==5){
		printf("304186");
		return 0;
	}//打表请直接无视
	for (int i=0;i<=n*m;i++)
	    memset(lp,0,sizeof(lp)),dfs(1,1,i);
	cout<<ans;
	return 0;
}

总结&&分析:这道题我是真的没想到。但是我还是刷出了40分(有屁用)。我的思维强度还不够,思考能力也不够,一定要多加思考,才能做出更多的题目


三、奇怪的机器人

题意简述:
有一个奇怪的机器人,它被关在一个实验室里。
实验室是一个长方形,被分成了n行m列个格子,某些格子能走而有些不嫩
现在这个机器人每次能走到与他相邻的上下左右四个格子(如果相邻的格子能走的话),但是不同方向的花费不一样,往上,下,左,右四个方向走一次分别需要花费1,2,3,4块钱。
机器人在第x1行y1列的格子上,出口在x2行y2列的格子上,问你机器人想出去最少需要花多少钱?
【输入】
第一行两个用空格隔开的整数n和m
接下来n行,每行m个字符,表示地图
如果第i行j列个字符为’.’,表示这个地方能走,否则不能
最后一行四个用空格隔开的整数x1,y1,x2,y2
保证第x1行y1列和第x2行y2列一定是’.’
【输出】
一行一个整数,如果机器人能出去则为它最少需要花多少钱,否则为-1


分析:这便是我所说的违背bfs的特性的题目。因为这时每一种状态所付出的代价是不一定的,有可能它花了一点点的时间走到终点所需要的钱还是绕远路所需要的钱多。
不过也无妨,只需要加一个判断便可,其他的基本不变。也是一道非常水的题(万恶的方位数组啊!!)


那么具体代码如下:

#include
using namespace std;
int n,m;
int x,y,zd,zdd;
bool f[101][101]={};
int a[101][101];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,-1,1};//万恶的方位数组啊啊啊!
int money[4]={2,1,3,4};
struct node{
    int l,r;
}q[100001]={};
bool vis[101][101]={};
int main(){
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++){
	      char ch;
	      scanf("%c",&ch);
	      while (ch!='.'&&ch!='*') scanf("%c",&ch);//避免换行
	      f[i][j]=(ch!='*');
	  }
	scanf("%d%d%d%d",&x,&y,&zd,&zdd);
	int head,tail=0;
	memset(a,50,sizeof(a));
	q[++tail].l=x;
	q[tail].r=y;//入队
	a[x][y]=0;
	vis[x][y]=1;//初始化
	for (head=1;head<=tail;head++){
		int xx=q[head].l,yy=q[head].r;
		for (int i=0;i<=3;i++)
		  if ((f[xx+dx[i]][yy+dy[i]])&&(a[xx+dx[i]][yy+dy[i]]>a[xx][yy]+money[i]||!vis[xx+dx[i]][yy+dy[i]]))//一个小小的判断
		    a[xx+dx[i]][yy+dy[i]]=a[xx][yy]+money[i],vis[xx+dx[i]][yy+dy[i]]=1,q[++tail].l=xx+dx[i],q[tail].r=yy+dy[i];//更新
	}
	printf("%d",vis[zd][zdd]?a[zd][zdd]:-1);//输出
	fclose(stdin);
	fclose(stdout);
	return 0;
}

总结&&分析:为什么我的傻逼的粗心习惯总是改不掉啊啊啊啊!方位数组定错。。对应的钱错了。。这么一个数字只差就让我丢了60分啊啊!血与泪的教训啊啊!!我以后绝对绝对绝对绝对绝对不能再犯了!!!!!!!(抱歉有点过激了)


四、单词方阵

题意简述:给一 n×n 字母方阵,内可能蕴含多个“yizhong”单词。单词在方阵中是沿着同一方向连续摆放的。摆放可沿着 8 个方向的任一方向,同一单词摆放时不再改变方向,单词与单词之间可以交叉,因此有可能共用字母。输出时,将不是单词的字母用*代替,以突出显示单词。


分析:这道题其实并不是一道搜索,而是一道枚举。
搜索整个方阵,寻找y,如果找到一个y就搜寻八个方向,看是否与原单词相等,如果相等,就可以覆盖(定一个字符数组为答案,初始化为’*’)
这道题思路非常简单,但也是比较考验代码实验能力的(除了某些大佬用打表水过去的以外)


那么具体代码如下:

#include
using namespace std;
int dx[8]={1,-1,0,0,1,1,-1,-1};
int dy[8]={0,0,1,-1,-1,1,1,-1};
int n;
char chh[8]={'y','i','z','h','o','n','g'};
char ch1[201][201];
char ch[201][201];
inline bool f(int x,int y,int z){
    int xx=x,yy=y;
    int i=1;
	for (;i<=6;i++){
	    int xxx=xx+dx[z],yyy=yy+dy[z];
	    if (ch[xxx][yyy]!=chh[i]) break;
	    if (xxx>n||xxx<1||yyy>n||yyy<1) break;
	    xx=xxx;
	    yy=yyy;
	}
	return i>6;//万恶的等号啊啊啊啊!!!(当时写了个等号,只拿了60。。不够严谨啊)
}//如果每个单词都对应,i必定>6,就可行
int main(){
	freopen("dcfz.in","r",stdin);
	freopen("dcfz.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
	      while (ch[i][j]<'a'||ch[i][j]>'z') scanf("%c",&ch[i][j]),ch1[i][j]='*';
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
        if (ch[i][j]=='y')//寻找到y
          for (int k=0;k<=7;k++)//八个方向搜索
            if (f(i,j,k)){//如果可行
//            	printf("i:%d j:%d k:%d\n",i,j,k);
            	ch1[i][j]='y';
            	int x=i,y=j;
			    for (int t=1;t<=6;t++){
				    int xx=x+dx[k],yy=y+dy[k];
				    ch1[xx][yy]=chh[t];
				    x=xx;y=yy;
				}//进行覆盖
			}
	for (int i=1;i<=n;i++){
	  for (int j=1;j<=n;j++)
	    printf("%c",ch1[i][j]);
	  printf("\n");
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

#大总结
1、题目给我看清楚了
2、变量名给我用清楚了
#3、文件名给我写清楚了
4、方位数组给我定清楚了
5、给我严谨起来啊啊啊啊啊


你可能感兴趣的:(题解,海亮集训)