关于搜索的题解

一、八皇后 Checker Challenge

假设有一张n*n表格,上面全是0代表空,然后我们一行一行的遍历,每一行放一个并做好标记

在标记的时候,实际上我们只需要标记三个方向,,左下,正下,右下(应为一行只会有一个皇后,并且已经放好皇后的行数在没有回溯的情况下不会受到接下来皇后的放置的影响)

标记是通过加一,也就是每有一个皇后的不能放的范围触及到那里,就加一,这样回溯的时候就减一,就会避免掉回溯把几个皇后的触及范围全部消掉。

#include<stdio.h>
int map[20][20]= {0},n,result[20],count=0; //统计结果的个数
int dix[]= {1,1,1}; //三方位
int diy[]= {-1,0,1};
void draw(int x,int y,int flag)//当flag=1时表示标记,为-1时表示回溯重置
{
    map[x][y]=map[x][y]+flag;//标记本身位置
    for(int i=0; i<3; i++) //定位三方位
    {
        for(int j=1; j<=n-x; j++)
        {
            int a=x+dix[i]*j;
            int b=y+diy[i]*j;
            if(a>0&&a<=n)//判断x轴是否越界
                if(b>0&&b<=n)//判断y轴是否越界
                    map[a][b]=map[a][b]+flag;
        }
    }
}
void run(int present)//现如今的行数
{
    if(present==n+1)//当每一行都放置了皇后
    {
        count++;
        if(count<=3)
        {
            for(int i=1; i<=n; i++)
               printf("%d ",result[i]);
            printf("\n");
        }
        return ;
    }
    for(int i=1; i<=n; i++) //继续查找
    {
        if(map[present][i]==0)//当遍历到可以放置皇后的位置
        {
            result[present]=i;
            draw(present,i,1);//标记
            run(present+1);
            draw(present,i,-1);//回溯重置
        }
    }
}
int main()
{
    scanf("%d",&n);
    run(1);//从第一行开始选择位置
    printf("%d",count);
    return 0;
}

 二、考前临时抱佛脚

 我的初步想法是贪心,但是很明显不行,这个题可以将它转化为01背包问题,将一个科目总时间的一半看成一个背包,当两边脑子的时间最接近的时候就是最短的时间

#include<stdio.h>
int max(int x,int y)
{
    if(x>y)
        return x;
    else
        return y;
}
int main()
{
    int a[10],sum=0,mm[1000]={0};
    for(int i=1;i<=4;i++)
        scanf("%d",&a[i]);
    for(int i=1; i<=4; i++) //一科一科的算需要多少时间
    {
        int s=0,m[100];
        for(int j=1; j<=a[i]; j++)
        {
            scanf("%d",&m[j]);
           s=s+m[j];
        }
        if(a[i]==1)//当只有一道题目时,就为一道题本身
        {
            sum=sum+m[1];
            continue;
        }
        for(int j=1;j<=a[i];j++)
            for(int k=s/2;k>=m[j];k--)//取两边脑袋最相近的时候
               mm[k]=max(mm[k],mm[k-m[j]]+m[j]);//01背包
        sum=sum+s-mm[s/2];//一边脑袋最接近一半但不超过一半,另一半脑袋就是这个科目所需要的最短时间
    }
    printf("%d",sum);
    return 0;
}

三、马的遍历

因为没下过象棋,去网上查了一下马的走向,大概是这样的方位

用的广度搜索,深度搜索数据过大的时候时间复杂度很高

#include<stdio.h>
int n,m,endx,endy,mp[402][402],book[402][402]= {0},head=1,end=1;
int dx[8]= {-2,-1,1,2,2,1,-1,-2},dy[8]= {-1,-2,-2,-1,1,2,2,1};//马的八个方向
struct code
{
    int x;
    int y;
    int step;
} aa[160100];//开大一点
int main()
{
    scanf("%d%d%d%d",&n,&m,&endx,&endy);
    aa[end].x=endx;
    aa[end].y=endy;
    aa[end].step=0;
    end++;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            mp[i][j]=-1;
    book[endx][endy]=1;//标记已经走过的路
    while(head<end)//当无路可走的时候退出来
    {
        for(int i=0; i<8; i++)//枚举8个方向
        {
            int nextx=aa[head].x+dx[i];
            int nexty=aa[head].y+dy[i];
            if(book[nextx][nexty]==0&&nextx>0&&nextx<(n+1)&&nexty>0&&nexty<(m+1))//判断是否越界并且没有走过
            {
                book[nextx][nexty]=1;//标记此点已经走过
                aa[end].x=nextx;
                aa[end].y=nexty;
                aa[end].step=aa[head].step+1;
                end++;//队列向后延申
            }
        }
    head++;
    }
    for(int i=1; i<=end; i++)
        mp[aa[i].x][aa[i].y]=aa[i].step;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        printf("%-5d",mp[i][j]);//注意距宽
        printf("\n");
    }
    return 0;
}

四、PERKET

可恶啊这道题我一看就以为要用贪心,结果错了;后来我仔细分析,这道题它通过添加材料使酸度和苦度最为接近,那么在做选择的时候无非就是两种情况,一种是加了,一种是没加,所以我们只要用搜索将所有情况全部遍历出来就可以了。

 像这样子,每种情况的最后要与最小值比较,取最小值

#include<stdio.h>
#include<math.h>
int chage(int x,int y)//取较小值的函数
{
    if(x>y)
        return y;
    else
        return x;
}
int min=1000000000,n;
struct ppp//酸苦度结构体
{
    int s;
    int b;
} z[100];
void dfs(int i,int nows,int nowb,int book)
{
    if(i>n)//设立终止条件
    {
        if(book==-n)//这是一个都没选的时候
            return;
        int cha=abs(nows-nowb);
        min=chage(cha,min);//更新最小值
        return;
    } dfs(i+1,nows,nowb,book-1);//没选
    dfs(i+1,nows*z[i].s,nowb+z[i].b,book+1);//选了这个材料

}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&z[i].s,&z[i].b);
    dfs(1,1,0,0);//酸度初始值应为1,苦度初始值为0,做一个记号防止一个都没选
    printf("%d",min);
    return 0;
}

五、 迷宫

这个题和搜索的经典例题有一点不同,没有直接给出地图,出于我的知识浅薄,我在后面直接构建了一个0地图,再插入1的阻碍,再按一般套路来写就可以了

#include<stdio.h>
int sum=0,n,m,t,sx,sy,fx,fy,next[4][2]={{-1,0},{1,0},{0,-1},{0,1}},book[100][100],mp[100][100];
void dfs(int x,int y)
{
    int tx,ty;
    if(x==fx&&y==fy)
    {
        sum=sum+1;//计数器
        return;//回到上一次递归的地方
    }
    for(int i=0;i<4;i++)
    {
        tx=x+next[i][0];//下一步走向
        ty=y+next[i][1];
        if(tx>m||tx<1||ty>n||ty<1)//判断是否越界
            continue;
        if(mp[tx][ty]==0&&book[tx][ty]==0)//没有走过并且没阻碍
        {
            book[tx][ty]=1;//标记走过
            dfs(tx,ty);
            book[tx][ty]=0;//回溯
        }
    }
    return ;
}
typedef  struct node
{
    int x;
    int y;
}point;
int main()
{

   point a[1000];
   scanf("%d%d%d",&n,&m,&t);//n为行,m为列,t为多少个障碍
   scanf("%d%d%d%d",&sx,&sy,&fx,&fy);//起点坐标sx,sy,终点坐标fx,fy
    for(int i=1;i<=t;i++)
    scanf("%d%d",&a[i].x,&a[i].y);
    for(int i=1;i<=n;i++)//根据信息创建一个地图
    {
        for(int j=1;j<=m;j++)
        {
            mp[i][j]=0;
        }
    }
    for(int i=1;i<=t;i++)
    {
        mp[a[i].x][a[i].y]=1;//设置障碍
    }
    book[sx][sy]=1;//标记起点是已经走过的路
    dfs(sx,sy);
    printf("%d",sum);
    return 0;
}

六、单词方正

这个题一看就是要用搜索8个方向搜,但是我有一个奇怪的想法,我发现所有的字符串要么顺序或者逆序横着相连,要么隔着n竖向相连要么隔着n+1和n-1斜向相连就只有这八种,那么只要将二维转一维然后在特定的位置判断是不是有我特定的字母就可以了

#include<stdio.h>
int main()
{
    char a[1000050],b[200][200];
    int n,k=1;
    scanf("%d",&n);
    for(int j=1; j<=n; j++)
    {
        for(int i=1; i<=n; i++)
        {
            scanf(" %c",&b[j][i]);
            if(b[j][i]!='\n')//将二维数组转化为一维数组
            {
                a[k]=b[j][i];
                k++;
            }
        }
    }

    for(int i=1; i<k; i++)
    {
        if(a[i]=='y')//字符串顺序相连
        {
            if(a[i+1]=='i'&&a[i+2]=='z'&&a[i+3]=='h'&&a[i+4]=='o'&&a[i+5]=='n'&&a[i+6]=='g')//字符串横向相连
            {
                for(int j=0; j<7; j++)
                    a[i+j]-=32;
            }
             else if(a[i+n]=='i'&&a[i+2*n]=='z'&&a[i+3*n]=='h'&&a[i+4*n]=='o'&&a[i+5*n]=='n'&&a[i+6*n]=='g')//字符串竖向相连
            {
                for(int j=0; j<7; j++)
                    a[i+n*j]-=32;
            }
            else if(a[i+(n+1)]=='i'&&a[i+2*(n+1)]=='z'&&a[i+3*(n+1)]=='h'&&a[i+4*(n+1)]=='o'&&a[i+5*(n+1)]=='n'&&a[i+6*(n+1)]=='g')//字符串斜向相连
            {
                for(int j=0; j<7; j++)
                    a[i+(n+1)*j]-=32;
            }
        }
        else if(a[i]=='g')//字符串逆序相连
        {
            if(a[i+1]=='n'&&a[i+2]=='o'&&a[i+3]=='h'&&a[i+4]=='z'&&a[i+5]=='i'&&a[i+6]=='y')//字符串横向相连
            {
                for(int j=0; j<7; j++)
                    a[i+j]-=32;
            }

            else if(a[i+n]=='n'&&a[i+2*n]=='o'&&a[i+3*n]=='h'&&a[i+4*n]=='z'&&a[i+5*n]=='i'&&a[i+6*n]=='y')//字符串竖向相连
            {
                for(int j=0; j<7; j++)
                    a[i+n*j]-=32;
            }
             else if(a[i+(n+1)]=='n'&&a[i+2*(n+1)]=='o'&&a[i+3*(n+1)]=='h'&&a[i+4*(n+1)]=='z'&&a[i+5*(n+1)]=='i'&&a[i+6*(n+1)]=='y')//字符串斜向相连
            {
                for(int j=0; j<7; j++)
                    a[i+(n+1)*j]-=32;
            }
        }
    }
    for(int i=1;i<k;i++)
    {
        if(a[i]>=97)
            printf("*");
        else
            printf("%c",a[i]+32);
        if(i%n==0&&i!=n*n)
            printf("\n");
    }
    return 0;
}

 七、 自然数的拆分问题

 根据这个模板写

#include<stdio.h>
int n,a[10000];
void dfs(int m,int start,int num)
{
    if(m==1)//存放终止条件
    {
        for(int i=0;i<num-1;i++)
        printf("%d+",a[i]);
        printf("%d\n",a[num-1]);//最后一个数的格式
        return;
    }
    for(int i=start;i<m&&i<n;i++)
    {
        a[num]=i;//将数一个一个存进数组
        m=m-i;
    //   printf("%d\n",m);
        dfs(m,i,num+1);//递归
        m=m+i;//回溯,撤销处理结果
    }
}
int main()
{
    scanf("%d",&n);
    dfs(n+1,1,0);//最开始从1开始搜,在最开始一个数组的个数有0个
    return 0;
}

八、Lake Counting S

我们只需要遍历整个地图,只要遇到w,计数器就加一,然后将与w相连的所有w全部填上,然后再继续遍历其他的地方

#include<stdio.h>
int dx[8]= {-1,-1,-1,0,0,1,1,1},dy[8]= {-1,0,1,-1,1,-1,0,1}; //8个方向的坐标移动
int m,n,sum=0;//输入长和宽
char mp[105][105];
void dfs(int x,int y)
{
    mp[x][y]='.';//将水坑填上
    for(int j=0; j<8; j++) //8个方向
    {
        int nextx=x+dx[j];//新坐标
        int nexty=y+dy[j];
        if(nextx>0&&nextx<(m+1)&&nexty>0&&nexty<(n+1))//判断是否越界
        {
            if(mp[nextx][nexty]=='W')//判断下一步是不是水坑
            dfs(nextx,nexty);
        }
    }
}
int main()
{
    scanf("%d%d",&m,&n);//输入长和宽
    getchar();
    for(int i=1; i<=m; i++)
        for(int j=1; j<=n+1; j++)
            scanf("%c",&mp[i][j]);
    for(int i=1; i<=m; i++)//遍历整个地图
    {
        for(int j=1; j<=n; j++)
        {
            if(mp[i][j]=='W')//当遇到水坑
            {
                sum++;//计数器加一
                dfs(i,j);
            }
        }
    }
    printf("%d",sum);
}

九、填涂颜色

从(1,1)开始,然后遍历每个点,如果发现这个点可以走出去,就判断此点是外围的点,然后做标记,但是遇到(1,1)就是墙这种情况,从(1,1)开始撞墙,然后导致有一部分无法遍历,所以遍历要将整个外围都遍历到

#include<stdio.h>
int n;
int mp[100][100],book[100][100];
int next[4][2]= {{0,1},{1,0},{0,-1},{-1,0}};
void dfs(int x,int y)
{
	int tx,ty;
    if(mp[x][y]==1)//等于1,说明撞墙,需要返回
    {
        return;//回到上一次递归处
    }
    mp[x][y]=3;//不等于1,说明这个点为图像外面的0,把这类点统一变成3
    for(int k=0; k<=3; k++) //枚举4种走法
    {
            tx=x+next[k][0];//下一步走的方位
            ty=y+next[k][1];
        if(tx>=1&&ty>=1&&tx<=n&&ty<=n&&book[tx][ty]==0)//给予边界
		{
            book[tx][ty]=1;//标记此处
            dfs(tx,ty);//再次递归,
		}
        }
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            scanf("%d",&mp[i][j]);
    for(int i=1;i<=n;i++)//只递归(1,1)的话,就会一开始就碰壁,然后返回,所以说,需要环绕一周,这样子就可以把闭合图像周围的所有0都给标记上
    {
		dfs(1,i);
		dfs(n,i);
		dfs(i,1);
		dfs(i,n);
	}
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(mp[i][j]==3)
                printf("0 ");
            else if(mp[i][j]==0)
                printf("2 ");
            else
                printf("%d ",mp[i][j]);
        }
        printf("\n");
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值