【考题 题解】奇怪机器人&单词方阵&修建道路

前言

欢迎关注我(小周猪猪)的博客,同时也欢迎关注为你们讲解的乔治大佬(心畅)的博客。如果对于原来的考试,你有不会的题,可以参考数字游戏题解搜索题解递归地推题解或者更多讲课内容的笔记与讲解。

1.奇怪的机器人

看到这些方阵的题,我们便自然可以联想到用搜索来解决。但是实际上这道题可以同时用 DFS D F S BFS B F S 来解决,这里采用较为简洁的 DFS D F S +剪枝(记忆化)的做法。

深搜的基本步骤:如果我们用 DFS D F S ,那么在递归的过程中我们可以用4个参数来记录当前走到的行,列和总花费。如果上一个状态是边界,那么直接判断答案是否合法,再退出即可。否则再在当前状态的基础上,往上下左右四个方向走,如果可以走,那么就往下一个状态进行转移。

你可以认为上述内容属于废话,因为许多棋盘上的题都是用这种思路来做的。涉及到上下左右,我们便可以用方位数组简便程序(在程序中用 dx d x dy d y 表示),也用方位数组记录行走的花费(用数组 cost c o s t 表示)

但是你也可以知道,按照上述会出现大量非最优解,重复或多余的运算,我们可以考虑剪枝。

1 1 .如果参数的状态相同,我们可以退出。用 vis v i s 数组判断重复。
2 2 .如果当前花费比上一次到达这一个点的花费还要不优的话,则退出。用 F[i][j] F [ i ] [ j ] 记录到 i i j j 的最优花费

代码如下:

#include<bits/stdc++.h>
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;
}//判断坐标(X,Y)是否超出地图范围
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<<ans;
    fclose(stdin);
    fclose(stdout);
    return 0;
}

单词方阵

这是一道简单的枚举,然后按照题意去模拟即可:枚举每一个字符串“ yizhong y i z h o n g ”中起点” y y ”的位置,然后向八个方向搜索即可。

代码如下:

#include<bits/stdc++.h>
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<<a[i][j];
                else cout<<'*';
        cout<<endl;
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

修复线路

这道题目一般难就难在如何想到怎么去建图,建图之后跑一遍最短路就完事了。

最后的答案问我们自己修复的最长线路的最小值,其实就是海亮有很多种不同的修剪方案,在这个自己修建的方案中有一个自己修剪的最大长度,现在就要求这个线路最小。相信对于”最大“”最小“的字眼你一定知道的是二分答案

我们暂且不管二分答案,我们知道海亮有一条自己修建的最大线路 len l e n ,其实就说明了自己修剪的每一条线路的长度都 len ≤ l e n ,只要小于这个数值修多少条线路都是没有问题的。那么,为了保证条件可以满足,我们必须让海亮修建的每一条线路都 这个数,保险公司修的都 这个数(显然这种情况是成立的)。但是保险公司修的线路都是有限制的,所以我们希望保险公司修的线路越小越好。那么关键就是,如何知道这个保险公司修复的最小线路呢?

我们可以考虑用图论的算法解决,将任何一个大于 len l e n 的边都设为 1 1 ,否则设为 0 0 .不存在边则设置为 + + ∞ ,跑一遍最短路,最后的结果就是保险公司要修剪的道路。为什么?因为路都是由 0 0 1 1 组成的, 0 0 表示不用修, 1 1 表示要修,那么最后的答案就是由很多个 1 1 组成,便是需要保险公司修剪的道路的条数 ans a n s 。如何判断答案是否合法,只要 ansk a n s ≤ k 即可。——这就是 check c h e c k 函数的构成。

至于边是多少,二分答案即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int Len;
int n,p,k;
int Vis[1200];
int dis[1200];
int a[1200][1200];
int Map[1200][1200];
inline void Fillchar()
{
    memset(Vis,0,sizeof(Vis));
    memset(Map,100,sizeof(Map));
    memset(dis,100,sizeof(dis));
} 
inline void Makemap()
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            if (a[i][j]<a[0][0])
                Map[i][j]=(int)a[i][j]>Len;
    dis[1]=0;Vis[1]=0;
    for (int i=2;i<=n;i++) dis[i]=Map[1][i]; 
    return;
}//建图 
inline void Error()
{
    cout<<"-1\n";
    exit(0);
}
int Shortest_Path()
{
    for (int now=1;now<=n;now++)
    {
        int Min=a[0][0],pnt=0;
        for (int i=1;i<=n;i++)
            if (!Vis[i] && dis[i]<Min)
                Min=dis[i] , pnt=i;
        if (Min==a[0][0] || !pnt) break;
        else Vis[pnt]=1;
        for (int i=1;i<=n;i++)
            if (!Vis[i] && dis[pnt]+Map[pnt][i]<dis[i])
                dis[i]=dis[pnt]+Map[pnt][i];
    }
    return dis[n];
}//采用无堆优化且邻接矩阵存储的迪杰斯特拉算法 
bool Check()
{
    int ans;
    Fillchar();
    Makemap();
    ans=Shortest_Path();
    return ans<=k;
}//二分答案的验证函数 
int main()
{
    cin>>n>>p>>k;
    memset(a,100,sizeof(a));
    for (int i=1;i<=p;i++)
    {
        int x,y,v;
        cin>>x>>y>>v;
        a[x][y]=a[y][x]=v;
    }//邻接矩阵构建无向图 
    int Left=0,Right=a[0][0];
    while (Left+1<Right)
    {
        Len=(Left+Right)>>1;
        if (Check()==true) Right=Len;
        else Left=Len;
        if (Len<=0) Error();
    }
    Len=Left;
    if (Check()) cout<<Left<<"\n";
    else cout<<Right<<"\n";//二分答案修建最大路径长度 
    return 0;
}

后记

关注猪猪的博客,我们一起交流哦~
同时欢迎对猪猪的博客进行点赞,评价,指正和访问哦~

    原文作者:道路修建问题
    原文地址: https://blog.csdn.net/Ronaldo7_ZYB/article/details/81809193
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞