AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 5854. 相对论大师    原题链接    简单

作者: 作者的头像   可是苦日子就像路易十六 ,  2025-06-10 14:38:30 · 甘肃 ,  所有人可见 ,  阅读 20


2


蒟蒻在没学之前用普通bfs卡最后一个数据TLE,因此在学过之后决定写一写心得。欢迎大佬指出错误,感激不尽。(代码C++实现)


题目描述

对于题目中给到的例子:

鱼越大,鱼刺越大;鱼刺越大,肉越少;肉越少,鱼越小;所以鱼越大,鱼越小

我们可以其看作:
题解1.png

如若我们将【鱼越大】【鱼刺越小】 这样的一个个论点看作一个个点,那么我们可以发现,这其实就是一张图;

那么我们的问题也就成功的转化为了:

在这张图中寻找一条由起点到终点距离最短的路径。


转换后的解题思路

我们由题可知,并不是每一个点都有终点:
以 xxxx flag 为起点的路径 必须以 xxxx !flag 作为结尾

我们可以遍历每一个点,然后以此点作为起点,通过bfs来搜索出是否存在完整路径。并把以此点为起点的完整路径的最短距离计算出来。遍历完所有的点后,然后再打印出所有起点中距离最小的那个。


解题步骤

我们将这道题分成三个步骤。
1.如何将由字符串表示的词条映射为图中的一个点
2.如何搜索
3.如何打印


① 将由字符串表示的词条映射为图中的一个点

我们可以用 unordered_map

unordered_map<A,B> 是一种哈希映射 我们可以通过 类型A来找到B 详细操作为 d【a】=b

举个例子:

unordered_map< string , int > d;
string str="YuCi 0";
d[str]=1;

此时我们就通过 unordered_map 将 YuCi 0 映射为了 数字 1
我们遍历图的时候,想知道 YuCi 0 可以到达哪些词条,就可以直接访问 head[1]的邻接表查看。
同时,我们要对映射进行一个备份。
开一个结构体数组e[],e[t]就存储着映射为 数字 t 的论点的信息。
以便于后面的起点终点判断和字符串打印。


②如何搜索

我们可以遍历每一个点,然后以此点作为起点,通过bfs来搜索出是否存在完整路径。并把以此点为起点的完整路径的最短距离计算出来。
不了解的可以移步 y总的【算法基础课】-【图的bfs】。


③如何打印

遍历完所有的点时,在每次更新完路径后,将此点的前置点标记在此点处(类似于链表)
比如:dist[j]=dist[t]+1;
我们就知道j点的新路径是由t点发展来的。因此,node[j].prev[a]=t;
(prev后面的数组[a]表示的是他是以点a为起点的路径)
然后在打印时,每次递归其前置点,打印此点词条,然后继续递归,直至前置点与起始点重合。


让我们看看代码是如何实现的。

C++

#include<bits/stdc++.h>

using namespace std;


const int N=1005*2;//有向图,一条推论由两个点(两个论点)和一条边形成,因此N条推论实际最多有2000个点。

struct Node     //标准的邻接表存贮图(我习惯了结构体写链表,见谅sry)
{
    int next;
    int point;
};
Node node[N];
int head[N],tot;

void insert(int x,int y)//图的存储函数
{
    node[++tot].next=head[x];
    head[x]=tot;
    node[tot].point=y;
}

struct Entry    //这个结构体就是我们上面说的结构体数组e[],用于存储着映射为 数字 t 的字符串的信息。
{
    string entry;   //论点的字符串
    string flag;    //论点后面的0/1
    int prev[N];//用于后面标记前置点序号
};
Entry e[N];

unordered_map <string , int> d; //推论与数字之间完成映射的重要工具
int cnt;

int n;
int ed[N];//ed[a]=b 以第a个点为起点的路径的结尾是点b

int dist[N];//bfs中用于存储步数的数组

int bfs(int a)  //bfs宽搜函数
{
    queue<int> q;
    q.push(a);

    memset(dist,-1,sizeof dist);

    dist[a]=0;
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        for(int i=head[t];i!=-1;i=node[i].next)
        {
            int j=node[i].point;
            if(dist[j]!=-1) continue;//dist【】=-1意味着未被遍历过
            dist[j]=dist[t]+1;
            e[j].prev[a]=t;//记录第a个点为起点的每个点的前置点
            if(e[a].entry==e[j].entry && e[a].flag!= e[j].flag)
            {
                ed[a]=j;
                return dist[j];
            }
            q.push(j);
        }
    }
    return 0x3f3f3f3f;//如果没有找到完整路径就返回一个很大的数,
    //意味着无穷远,无法到达,计算最小距离的时候自然就不需要顾及它了
}

void printt( int t,int sta,int ed )//t:当前层 sta:起始层 ed:终点层
{
    if(t!=sta)
        printt(e[t].prev[sta],sta,ed);
    if(t!=sta && t!=ed) //因为输出是 a—>b;b—>c;c—>d;这样的模式,
    //因此不在起始层和终点层的论点要打印两次,
    //一次作为一个推论的终点被引出,一次作为一个推论的起点引出下一个论点。
    {
        cout<<e[t].entry<<' ';
        cout<<e[t].flag<<' ';
    }
    cout<<e[t].entry<<' ';
    cout<<e[t].flag<<' ';

}
int main()
{
    memset(head,-1,sizeof head);//初始化图
    cin>>n;
    while(n--)
    {
        string s1,s2,s3,s4;
        cin>>s1>>s2>>s3>>s4;
        string s5=s1+' '+s2;
        string s6=s3+' '+s4;
        if(!d.count(s5))    //d.count()用于勘察此论点是否出现过。

        {       //如果没有出现过
            d[s5]=++cnt;    //为他注册一个新的映射
            e[cnt].entry=s1;//存贮新论点的信息
            e[cnt].flag=s2;
        }
        if(!d.count(s6))
        {
            d[s6]=++cnt;
            e[cnt].entry=s3;
            e[cnt].flag=s4;
        }
        insert(d[s5],d[s6]);
    }

    int mmin=INT_MAX,mmin_bh=-1;
    for(int i=1;i<=cnt;i++)
    {
        int dis=bfs(i);
        if(mmin>dis)    //比较一下以映射为数字i的论点为起点的映射到达终点的距离
                        //确保mmin中存贮的是最小的距离
                        //mmin_bh用于存储最短路径所在起点的数字
            {
                mmin=dis;
                mmin_bh=i;
            }
    }
    printt(ed[mmin_bh],mmin_bh,ed[mmin_bh]);
    cout<<"= "<<e[mmin_bh].entry<<' '<<e[mmin_bh].flag<<' '<<e[ed[mmin_bh]].entry<<' '<<e[ed[mmin_bh]].flag<<endl;
    return 0;
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息