【考题题解】深度优先搜索dfs 广度优先搜索bfs 枚举或查找

马的遍历

【问题描述】
有一个n*m的棋盘(1<=n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
【输入】
一行四个数据,棋盘的大小和马的坐标
【输出】
一个n*m的矩阵,代表马到达某个点最少要走几步(不能到达则输出-1
每行的m个数用空格隔开
【输入输出样例1】
input
3 3 1 1
output
0 3 2
3 -1 1
2 1 4

思路分析:这道题是一到广搜题:首先,求的是最短路径;其次,要遍历每一个节点,完全符合广搜节点扩展的算法原理.我们只需要在广搜扩展的时候求出每一个点dis,最后判断是否为0并按题目要求输出即可.实现难度不大,和广搜模板大致相似:
代码如下:

#include
using namespace std;
int n,m;
int hx,hy;
int dis[500][500];
int vis[500][500];
int head=1,tail=1;
struct horse{int x,y;};
horse q[10000000];
int dx[8]={-1,-2,-2,-1,1,2,2,1};
int dy[8]={2,1,-1,-2,-2,-1,1,2};
inline bool InMap(horse p){return p.x>=1&&p.x<=n&&p.y>=1&&p.y<=m;}
inline bool Candrump(horse p){return vis[p.x][p.y]==0;}
int main()
{
    freopen("horse.in","r",stdin);
    freopen("horse.out","w",stdout);
    cin>>n>>m>>hx>>hy;
    q[1]=(horse){hx,hy};
    vis[hx][hy]=1;dis[hx][hy]=0;
    while (head<=tail)
    {
        horse now=q[head];
        for (int i=0;i<8;i++)
        {
            horse next=(horse){now.x+dx[i],now.y+dy[i]};
            if (InMap(next)==false) continue;
            if (Candrump(next)==false) continue;
            q[++tail]=next;
            dis[next.x][next.y]=dis[q[head].x][q[head].y]+1;
            vis[next.x][next.y]=1;
        }
        head++;
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<m;j++)
            if (vis[i][j]==0)
                cout<<-1<<' ';
            else cout<' ';
        if (vis[i][m]==0)
            cout<<-1<else cout<m]<return 0;
}

中国象棋air【AHOI30%】

【问题描述】
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
【输入】
一行包含两个整数N,M,之间由一个空格隔开。
【输出】
总共的方案数
答案不超过int能容纳的最大值
【输入输出样例1】
1 3 7
【输入输出样例2】
3 2 49
【数据范围】
样例说明
除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。
数据范围
30% N=1
100% N,M<=6

至于这道题,dfs毕竟不是最好的方法.原因如下:
1.代码实现难度大
2.容易超市超时
因此对于个人而言:暴力+打表才是正解
考试成绩是90分,尽管有一个可怕的测试点6,6没有运行出来,但是3,3把265抄成了256痛失10分,那么便可以分享一下我原始暴力算法的计算过程:
1.枚举每一个点,能放则放,不能放则不放
2.至于判断的check过程,只要枚举每一个点判断它上下左右是否≥2的棋子数量
3.判重:会有重复.最大至36的状态很难用数组和变量表示.我们可以选择用字符串,将每一个状态用0,1记录下来,最有用map判重即可.(P党看到后可以学hash了;STL大法万岁)
90分程序如下:

#include
using namespace std;
int main()
{
    freopen("chess.in","r",stdin);
    freopen("chess.out","w",stdout);
    int a,b;
    cin>>a>>b;
    if (a==1&&b==1)cout<<2;
    if (a==1&&b==2)cout<<4;
    if (a==1&&b==3)cout<<7;
    if (a==1&&b==4)cout<<11;
    if (a==1&&b==5)cout<<16;
    if (a==1&&b==6)cout<<22;

    if (a==2&&b==1)cout<<4;
    if (a==2&&b==2)cout<<16;
    if (a==2&&b==3)cout<<49;
    if (a==2&&b==4)cout<<121;
    if (a==2&&b==5)cout<<256;
    if (a==2&&b==6)cout<<484;

    if (a==3&&b==1)cout<<7;
    if (a==3&&b==2)cout<<49;
    if (a==3&&b==3)cout<<256;
    if (a==3&&b==4)cout<<1081;
    if (a==3&&b==5)cout<<3481;
    if (a==3&&b==6)cout<<9367;

    if (a==4&&b==1)cout<<11;
    if (a==4&&b==2)cout<<121;
    if (a==4&&b==3)cout<<1081;
    if (a==4&&b==4)cout<<7343;
    if (a==4&&b==5)cout<<37441;
    if (a==4&&b==6)cout<<149311;

    if (a==5&&b==1)cout<<16;
    if (a==5&&b==2)cout<<256;
    if (a==5&&b==3)cout<<3481;
    if (a==5&&b==4)cout<<37441;
    if (a==5&&b==5)cout<<304186;
    if (a==5&&b==6)cout<<1859926;

    if (a==6&&b==1)cout<<22;
    if (a==6&&b==2)cout<<484;
    if (a==6&&b==3)cout<<9367;
    if (a==6&&b==4)cout<<149311;
    if (a==6&&b==5)cout<<1859926;
    if (a==6&&b==6)cout<<"RP++++++";
    fclose(stdin);
    fclose(stdout);
    return 0;
    }

接下来,分享一下我的暴力优化思路,可以将6,6的状态在5min左右时输出:
显然,每一行放棋子的个数必然≤2,不然必然会冲突,然后根据这一结论优化枚举状态即可.

#include
using namespace std;
int n,m,ans=1;
int visa[100];//记录行的放棋个数
int visb[100];//记录列的放棋个数 
int chess[100][100];
map< string , int >Vis;//map用于摆放状态状态判重 
inline bool Canput(int x,int y){return visa[x]<2&&visb[y]<2&&!chess[x][y];}
inline bool Onlyone()
{
    string S_now;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            S_now+=char(chess[i][j]+'0');
    if (Vis[S_now]) return false;
    Vis[S_now]++;
    return true;
}
void dfs()
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            if (Canput(i,j))
            {
                visa[i]++;
                visb[j]++;
                chess[i][j]=1;
                if (Onlyone())
                {
                    dfs();
                    ans++;
                }
                chess[i][j]=0;
                visa[i]--;
                visb[j]--;
            }   
} 
int main()
{
    cin>>n>>m;
    dfs();
    cout<return 0;
}

然后,我们就可以在0.5h内写出AC代码:

#include
using namespace std;
int main()
{
    freopen("chess.in","r",stdin);
    freopen("chess.out","w",stdout);
    int a,b;
    cin>>a>>b;
    if (a==1&&b==1)cout<<2;
    if (a==1&&b==2)cout<<4;
    if (a==1&&b==3)cout<<7;
    if (a==1&&b==4)cout<<11;
    if (a==1&&b==5)cout<<16;
    if (a==1&&b==6)cout<<22;

    if (a==2&&b==1)cout<<4;
    if (a==2&&b==2)cout<<16;
    if (a==2&&b==3)cout<<49;
    if (a==2&&b==4)cout<<121;
    if (a==2&&b==5)cout<<256;
    if (a==2&&b==6)cout<<484;

    if (a==3&&b==1)cout<<7;
    if (a==3&&b==2)cout<<49;
    if (a==3&&b==3)cout<<265;//考试的时候写了256本来AC的wuwuwuwuwu..... 
    if (a==3&&b==4)cout<<1081;
    if (a==3&&b==5)cout<<3481;
    if (a==3&&b==6)cout<<9367;

    if (a==4&&b==1)cout<<11;
    if (a==4&&b==2)cout<<121;
    if (a==4&&b==3)cout<<1081;
    if (a==4&&b==4)cout<<7343;
    if (a==4&&b==5)cout<<37441;
    if (a==4&&b==6)cout<<149311;

    if (a==5&&b==1)cout<<16;
    if (a==5&&b==2)cout<<256;
    if (a==5&&b==3)cout<<3481;
    if (a==5&&b==4)cout<<37441;
    if (a==5&&b==5)cout<<304186;
    if (a==5&&b==6)cout<<1859926;

    if (a==6&&b==1)cout<<22;
    if (a==6&&b==2)cout<<484;
    if (a==6&&b==3)cout<<9367;
    if (a==6&&b==4)cout<<149311;
    if (a==6&&b==5)cout<<1859926;
    if (a==6&&b==6)cout<<17525812;//没有6,6的数据,RP++啦 
    fclose(stdin);
    fclose(stdout);
}

然后鲜有人AC的题目就被water过了.


奇怪的机器人

【问题描述】
有一个奇怪的机器人,它被关在一个实验室里。
实验室是一个长方形,被分成了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
【输入输出样例1】
4 4
….
.**.
..*.
….
3 2 3 4 11
【输入输出样例2】
1 3
.*.
1 1 1 3 -1
【数据范围】
1<=n,m<=50

我道题奇迹般地爆0了……
显然,这不能用普通的dfs或者bfs,因为最先到达的状态并非当前最优解,因此我们需要使用记忆化搜索,将dfs设置3个参数,表示当前行,当前列,当前花费.并设F[x][y]表示运行到X,Y的位置的最优解sum,若当前运行状态的花费大于已运行花费则表明一定不是最优解,即记忆化搜索剪枝最优化剪枝.其次,我们还可以设vis[x][y][sum]来记录参数的状态,若该状态为1则表明已经搜索过,即可行性剪枝,代码实现难度同样不是很大:

#include
using namespace std;
int n,m;
int stx,sty;
int finx,finy;
int F[100][100];
int ans=pow(10,9);
char Map[100][100];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int Cost[4]={1,2,3,4};
int vis[100][100][2000];
inline bool check(int X,int Y)
{
    return X>=1&&X<=n&&Y>=1&&Y<=m;
}
void dfs(int x,int y,int sum)
{
    if (sum>=F[x][y]&&F[x][y]>0) return;
    if (vis[x][y][sum]==1) return;//剪枝
    F[x][y]=sum;vis[x][y][sum]=1;
    if (x==finx&&y==finy)
    {
        ans=min(ans,sum);
        return;
    }//边界
    for (int i=0;i<4;i++)
    {
        int nextx=x+dx[i];
        int nexty=y+dy[i];
        if (check(nextx,nexty)==true)
            if (Map[nextx][nexty]=='.')
                dfs(nextx,nexty,sum+Cost[i]);
    }//进一步搜索
    return;
}
int main()
{
    freopen("robot.in","r",stdin);
    freopen("robot.out","w",stdout);
    cin>>n>>m;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            cin>>Map[i][j];
    cin>>stx>>sty>>finx>>finy;
    dfs(stx,sty,0);
    if (ans==pow(10,9)) cout<<-1;
        else cout<return 0;
}

单词方阵

【问题描述】
给一 n×n 字母方阵,内可能蕴含多个“yizhong”单词。单词在方阵中是沿着同一方向连续摆放的。摆放可沿着 8 个方向的任一方向,同一单词摆放时不再改变方向,单词与单词之间可以交叉,因此有可能共用字母。输出时,将不是单词的字母用*代替,以突出显示单词。
【输入】
第一行输入一个数 n 。( 7≤n≤100)。
第二行开始输入 n×n 的字母矩阵。
【输出】
突出显示单词的 n×n 矩阵。
【输入输出样例1】
8
qyizhong
gydthkjy
nwidghji
orbzsfgz
hhgrhwth
zzzzzozo
iwdfrgng
yyyygggg *yizhong
gy******
n*i*****
o**z****
h***h***
z****o**
i*****n*
y******g

【输入输出样例2】
7
aaaaaaa
aaaaaaa
aaaaaaa
aaaaaaa
aaaaaaa
aaaaaaa
aaaaaaa
输出:全部是*

这道题其实就是模拟,若需定义一个算法名称那就是枚举和查找.
枚举:枚举每一个y
查找:根据枚举y的坐标8个方向查找是否合法并统计结果
代码如下:

 #include
using namespace std;
int n;
string s=" izhong";
char a[200][200];
int vis[200][200];
int dx[9]={0,-1,1,0,0,1,1,-1,-1};
int dy[9]={0,0,0,-1,1,1,-1,1,-1};
void f(int x,int y,int num)
{
    int stx=x,sty=y,flag=1;
    for (int i=1;i<=6;i++)
    {
        x+=dx[num];
        y+=dy[num];
        if (a[x][y]!=s[i]) 
        {
            flag=0;
            break;
        }
    }//判断该方向是否合法
    if (flag)
    {
        for (int i=1;i<=6;i++)
        {
            vis[stx][sty]=1;
            stx+=dx[num];
            sty+=dy[num];
        }
        vis[stx][sty]=1;
    }//如果合法则进行标记以便于最终输出结果
}
void find(int i,int j)
{
    if (i-6>=1) f(i,j,1);
    if (i+6<=n) f(i,j,2);
    if (j-6>=1) f(i,j,3);
    if (j+6<=n) f(i,j,4);
    if (i+6<=n&&j+6<=n) f(i,j,5);
    if (i+6<=n&&j-6>=1) f(i,j,6);
    if (i-6>=1&&j+6<=n) f(i,j,7);
    if (i-6>=1&&j-6>=1) f(i,j,8);//若条件合法,枚举8个方向
    return;
}
int main()
{
    freopen("dcfz.in","r",stdin);
    freopen("dcfz.out","w",stdout);
    cin>>n;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            cin>>a[i][j];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            if (a[i][j]=='y')
                find(i,j);//枚举y
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
            if (vis[i][j]) cout<else cout<<'*';
        cout<return 0;
}

你可能感兴趣的:(【考题题解】深度优先搜索dfs 广度优先搜索bfs 枚举或查找)