洛谷 搜索

dfs:枚举所有的解决方案。
实现:每次判断这步是否可行,不可行就返回;再判断是否为最后一步,是则返回或者输出;否则就是继续搜索。记得搜索结束要回溯。

bfs:找到最快的一条路径。
实现:将合法的下一步,都压入队列中,每次判断队列是否为空,不空则取出队首,继续判断。记录路径的话,就保存它的前驱点,等到终止时,再输出即可。

八皇后******
每行每列,所有对角线上 都只有一个皇后。
分析:我们逐行进行搜索。每搜索到一个位置,就将该列,和两个对角线 标记为已访问。
vis1[j]=vis2[i+j]=vis3[i-j+n]=1;
回溯时,清零即可。
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;

int vis1[maxn],vis2[maxn],vis3[maxn];
int n,ans[maxn],tot=0;//要求计算总方案数,并输出前三组(存在ans里)

void dfs(int i)
{
    if(i>n)
    {
        if(tot<3)
        {
            for(int j=1;j<=n;j++)
                cout<<ans[j]<<" ";
            cout<<endl;
        }
        tot++;
        return;
    }else{
        for(int j=1;j<=n;j++)
        {
            if(vis1[j]==0&&vis2[i+j]==0&&vis3[i-j+n]==0)
            {
                ans[i]=j;//记录某一行,走哪一列
                vis1[j]=vis2[i+j]=vis3[i-j+n]=1;
                dfs(i+1);
                vis1[j]=vis2[i+j]=vis3[i-j+n]=0;
            }
        }
    }
}

int main()
{
    cin>>n;
    memset(vis1,0,sizeof(vis1));
    memset(vis2,0,sizeof(vis2));
    memset(vis3,0,sizeof(vis3));
    dfs(1);
    cout<<tot;
}

迷宫总方案数*********
有路和墙,起点终点,求方案数。搜索跑图,一般套路。
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=6;
int mp[maxn][maxn];
bool vis[maxn][maxn];
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int total,fx,fy,sx,sy,T,n,m,l,r;//total计数器
void dfs(int x,int y)
{
    if(x==fx&&y==fy)//到达终点
    {
        total++;
        return;//返回,继续搜索;
    }
    else
    {
        for(int i=0;i<4;i++)//左,右,下,上四个方向;
        {
            int xx=x+dx[i],yy=y+dy[i];//下一步坐标
            if(vis[xx][yy]==0&&mp[xx][yy])//判断没有走过 且 可以走
            {
                vis[x][y]=1;//标记已走过
                dfs(xx,yy);
                vis[x][y]=0;//回溯清零
            }    
        } 
    }
}
int main()
{
    cin>>n>>m>>T;//题目中的 n,m长度宽度,T障碍个数 
    for(int i=1;i<=n;i++)//初始化全图均可走
        for(int j=1;j<=m;j++)
            mp[i][j]=1;
    cin>>sx>>sy;//起始x,y 
    cin>>fx>>fy;//结束x,y 
    for(int u=1;u<=T;u++)
    {
        cin>>l>>r;//l,r是障碍坐标;
        mp[l][r]=0;
    }
    dfs(sx,sy);
    cout<<total;//输出总数;
    return 0;
} 

单词方阵********
找出连成块的“yizhong”的单词方阵,其他单词用*替换。八个方向。
分析:先找到y 再找到i,确定了某个方向,才确认此方向可以考虑搜索
单词可以重叠,那么我们就需要记录一下单词的位置,成功结束后,再将标记一起置1
搜索时,试探下一个位置,和跑图类似
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;

char mp[maxn][maxn];
char str[]={"yizhong"};
int vis[maxn][maxn],ansx[maxn],ansy[maxn];//用于记录路径
int to[][2]={-1,0,0,-1,1,0,0,1,1,1,1,-1,-1,1,-1,-1};//左下右上,右上右下,左上,左下
int n;

void dfs(int x,int y,int k,int step)//k 所走的方向,step 所匹配到的字符串下标
{
    if(step==7)
    {
        for(int i=0;i<7;i++)
            vis[ansx[i]][ansy[i]]=1;
    }
    else
    {
        int xx=x+to[k][0];
        int yy=y+to[k][1];
        if(step==6 || mp[xx][yy]==str[step+1])
        {
            ansx[step]=x,ansy[step]=y;
            dfs(xx,yy,k,step+1);
        }
    }
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>mp[i];
    memset(vis,0,sizeof(vis));
    ///首先要在矩阵中找到 y i相连的块,后来再按照这个方向搜索才行
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(mp[i][j]=='y')
            {
                for(int k=0;k<8;k++)
                {
                    int xx=i+to[k][0],yy=j+to[k][1];
                    if(mp[xx][yy]=='i')
                        dfs(i,j,k,0);
                }
            }
        }
    }
    //out
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(vis[i][j])cout<<mp[i][j];
            else cout<<"*";
        }
        cout<<endl;
    }
}

填涂颜色**
整个图形只有0、1,把所有被1包围的0 变成2(只有一个1的环),并输出整张图。
分析:让我们找到被1包围的0的连通块,很难。
但是,我们会发现,因为只有一个环,那么环的外面的那些0可以找出来。
怎么找?从边界开始bfs,只要是0都标记了。最后那些没有被标记的0,也就是被1包围的0了。

#include<bits/stdc++.h>
#include<queue>
using namespace std;
const int maxn=40;
int n;
int vis[maxn][maxn],mp[maxn][maxn];
int to[][2]={1,0,0,1,-1,0,0,-1};
struct node
{
    int x,y;
};
void bfs(int sx,int sy)
{
    queue<node>q;
    q.push({sx,sy});
    vis[sx][sy]=1;
    mp[sx][sy]=-1;
    while(!q.empty())
    {
        node now=q.front();q.pop();
        for(int i=0;i<4;i++)
        {
            int xx=now.x+to[i][0],yy=now.y+to[i][1];
            if(!vis[xx][yy]&&mp[xx][yy]==0&&xx>=1&&xx<=n&&yy>=1&&yy<=n)
            {
                vis[xx][yy]=1;
                mp[xx][yy]=-1;
                q.push({xx,yy});
            }
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            cin>>mp[i][j];
            if(mp[i][j]) vis[i][j]=1;
        }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i][1]&&mp[i][1]==0)
            bfs(i,1);
        if(!vis[i][n]&&mp[i][n]==0)
            bfs(i,n);
        if(!vis[1][i]&&mp[1][i]==0)
            bfs(1,i);
        if(!vis[n][i]&&mp[n][i]==0)
            bfs(n,i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(mp[i][j]==-1) cout<<"0";
            else if(mp[i][j]==1) cout<<"1";
            else if(mp[i][j]==0) cout<<"2";
            if(j<n) cout<<" ";
        }
        cout<<endl;
    }
}

马的遍历*
马走日字,八个方向。
给出图的大小,和马的起点,求该点到涂上所有点的最短步数。
bfs(sx,sy);即可

#include<bits/stdc++.h>
#include<queue>
using namespace std;
const int maxn=404;
int n,m,sx,sy;
int vis[maxn][maxn],ans[maxn][maxn];
int to[][2]={1,2,2,1,-1,2,2,-1,1,-2,-2,1,-1,-2,-2,-1};
struct node
{
    int x,y,step;
}tt;
void bfs()
{
    vis[sx][sy]=1;
    ans[sx][sy]=0;
    queue<node>q;
    tt.x=sx,tt.y=sy,tt.step=0;
    q.push(tt);

    while(!q.empty())
    {
        node now=q.front();q.pop();
        for(int i=0;i<8;i++)
        {
            int xx=now.x+to[i][0],yy=now.y+to[i][1];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&!vis[xx][yy])
            {
                vis[xx][yy]=1;
                tt.x=xx,tt.y=yy,tt.step=now.step+1;
                q.push(tt);
                ans[xx][yy]=tt.step;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&sx,&sy);
    memset(vis,0,sizeof(vis));
    memset(ans,-1,sizeof(ans));
    bfs();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            printf("%-5d",ans[i][j]);
        }
        puts("");
    }
    return 0;
}

字串变换****
有两个字串A,B及一组字串变换的规则(至多6个规则)
若在10步(包含10步)以内能将A变换为B,则输出最少的变换步数

分析:对于每个当前待修改串,我们都有 其长度的修改方式。每个位置i都有可能用某种规则j替换。所以,这是个复制的过程,也是不断重复的,我们就写个函数来实现。(1)
bfs的知识就很普通,把合法的新串存起来就是,直到找到目标串为止(2)
有一个坑点,如果你不记录一下某个串是否已经搜索过了,而重复去搜,会tle。(3)

实现:(1)用string的方法,且首先判断一下合法性,再拼接string.substr(index_begin,index_end)返回。
(3)用map<string,int>ma;去标记一下。 从队列中取出字符串的时候判断剪枝下。
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=20;
string a,b;
string ta[maxn],tb[maxn];
map<string,int>ma;
int n=0,minn=0;
struct node
{
    string str;
    int step;
};
string checkk(const string &str,int i,int j)
{
    string ans="";
    if(i+ta[j].length()>str.length()) return ans;
    for(int k=0;k<ta[j].length();k++)
    {
        if(str[i+k]!=ta[j][k])
            return ans;
    }
    ans=str.substr(0,i);
    ans+=tb[j];
    ans+=str.substr(i+ta[j].length());
    return ans;
}
void bfs()
{
    queue<node>q;
    node x;
    x.str=a;x.step=0;
    q.push(x);
    while(!q.empty())
    {
        node now=q.front();
        q.pop();

        if(ma.count(now.str)==1) continue;//剪枝!!
        ma[now.str]=1;

        if(now.str==b)
        {
            minn=now.step;
            break;
        }
        for(int i=0;i<now.str.length();i++)
        {
            for(int j=0;j<n;j++)
            {
                string res=checkk(now.str,i,j);
                if(res=="")continue;
                x.str=res,x.step=now.step+1;
                q.push(x);
            }
        }
    }
}

int main()
{
    cin>>a>>b;
    while(cin>>ta[n]>>tb[n]) n++;
    bfs();
    if(minn==0||minn>10) cout<<"NO ANSWER!"<<endl;
    else cout<<minn<<endl;
}

数字三角形**
给出N,sum,求1-N的一个排列,使得每层为上一层相邻两个数相加,找出层数为N的值为sum的排列。
思路:(1)每层为上一层两个数相加:每个数字有重复加,考虑一下每个数字重复次数
分析得 规律即为杨辉三角。
(2)这么找出这样的排列呢?a).next_permutation生成排列。b).dfs
(3)注意剪枝,当前和已经超过sum 剪掉就好

#include<bits/stdc++.h>
using namespace std;

int yh[15][15],n,sum;
void yanghui()
{
    memset(yh,0,sizeof(yh));
    yh[1][1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            yh[i][j]=yh[i-1][j-1]+yh[i-1][j];
        }
    }
}
int vis[15];//每个数字i是否已经用到了
int res[15],f=0;//保存最后的那个排列序列
void dfs(int step,int ans)
{
    if(f||ans>sum) return;
    if(step==n+1 && ans==sum)
    {
        for(int i=1;i<=n;i++)
            cout<<res[i]<<" ";
        f=1;
        return;
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                res[step]=i;
                vis[i]=1;
                dfs(step+1,ans+i*yh[n][step]);
                vis[i]=0;
            }
        }
    }
}

int main()
{
    cin>>n>>sum;
    yanghui();
    dfs(1,0);
}

开了氧气优化的a方法代码

#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int res[15];
int yh[15][15];
void yanghui(int n)
{
    memset(yh,0,sizeof(yh));
    yh[1][1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            yh[i][j]=yh[i-1][j-1]+yh[i-1][j];
        }
    }
}
int main()
{
    int n,sum;
    cin>>n>>sum;
    yanghui(n);
    for(int i=1; i<=n; i++) res[i]=i;
    do {
        int tsum=0;
        for(int i=1;i<=n;i++)
            tsum+=res[i]*yh[n][i];
        if(tsum==sum)
        {
            for(int i=1;i<=n;i++)
                cout<<res[i]<<" ";
            break;
        }
    } while(next_permutation(res+1,res+1+n));
}

滑雪****
给出一张图,找出一条最长路线,路线上的值严格单调减。
分析:
(1)我们需要对于每个位置都要进行搜索,找出最短的一条。但是可能找到某个位置是,之前已经找出了,从这个位置出发的最长的一条路线,再重复搜索就是浪费时间,所有我们可以把这个位置开始的最大值记录下来。下次遇到了,直接返回就行。——记忆化搜索
dis[x][y]=d;
int dfs(int x,int y){
if(dis[x][y])return dis[x][y];

}
(2)dfs找出最长的那一条,每走一步就+1,我们可以利用带返回值类型的dfs进行优化,也不需要再用一个专门的标记表示某点是否走过了。 d=max(dfs(xx,yy)+1,d);

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
int n,m,mp[maxn][maxn],maxx;
int dis[maxn][maxn];
int to[][2]={1,0,0,1,-1,0,0,-1};

int dfs(int x,int y)
{
    if(dis[x][y])return dis[x][y];
    int d=1;
    for(int i=0;i<4;i++)
    {
        int xx=x+to[i][0],yy=y+to[i][1];
        if(xx<0||xx>=n||yy<0||y>=n||mp[xx][yy]>=mp[x][y])continue;
        d=max(dfs(xx,yy)+1,d);
    }
    dis[x][y]=d;
    return d;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            cin>>mp[i][j];

    memset(dis,0,sizeof(dis));
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            int d=dfs(i,j);
            if(d>maxx)
                maxx=d;
        }
    cout<<maxx;
}

吃奶酪*****
给出n组坐标,问从(0,0)出发走完所有坐标的最短距离。两点间距离,用距离公式计算。
分析:题意明显要用dfs,当前方案走完所有点step==n的话,就比较一下,更新最小值
当前的最短距离,存在数组里不合适,用返回值也不能保证是最短的,所以选择存在dfs形参里。同时这个值,可以提供来剪枝,如果当前搜索到的值,比当前最优解还不如,那就剪掉。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=20;

double dis_count(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int n,vis[maxn];
double ax[maxn],ay[maxn],ans=999999999999999.9,dis[maxn][maxn];

void dfs(int step,int now,double res)
{
    if(res>ans)return;//剪枝
    if(step==n)
    {
        ans=min(ans,res);
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            dfs(step+1,i,res+dis[now][i]);
            vis[i]=0;
        }
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>ax[i]>>ay[i];
    memset(dis,0,sizeof(dis));
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            if(i==j)continue;
            dis[i][j]=dis_count(ax[i],ay[i],ax[j],ay[j]);
        }
    }
    dfs(0,0,0.0);
    cout.setf(ios::fixed);
    cout<<fixed<<setprecision(2)<<ans;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值