• 美团2017年CodeM大赛-资格赛 NC13224 送外卖(记忆化搜索 or 预处理 dfs)


    在这里插入图片描述
    在这里插入图片描述

    题意:

    有一个人 初始位置为 0,他的 目的地是 n - 1

    给定两个表示 移动方案 的整数数列 a[0] ~ a[n-1]b[0] ~ b[n-1] ,在 每个小区 i 里他有 两种选择

      1. 选择 a向前 a[i] 个小区
      1. 选择 b向前 b[i] 个小区

    把每步的选择写成一个 关于字符 ‘a’ 和 ‘b’ 的字符串。求这人 到达小区 n - 1 的方案中字典序最小 的字符串

    • 没有合法的选择序列 时,输出 No solution!
    • 字典序最小的字符串无限长 时,输出 Infinity!
    • 否则,输出这个 选择字符串

    思路:

    首先一个很重要的点,题目虽然要求求出 字典序最小 的答案,但我们并不需要可以去求,因为在本题中,以 中序遍历 dfs 的顺序 遍历所有方案时,我们 得到的第一个答案一定字典序最小

    本题数据范围达到了 1e5,每一步有 两种选择,如果敲裸的 dfs 的话,复杂度达到了 2 ^ 1e5,这显然是不被允许的。考虑进行 两遍 dfs第一遍 dfs 预处理出从起点 0 出发,能够到达终点 n - 1 的所有方案中可途径的点,用一个 bool 数组 ok 来存储信息,ok[u] = true 表示 0n - 1 的某个方案中途径了 u。(由于有 标记数组 st 防止重复搜索,时间复杂度为 O(N)

    void dfs1(int u = 1)
    {
    	if (st[u]) return;	//搜过无须再搜
    	st[u] = true;	//必要的防止重复搜索标记
    	if (u == n) {	//一旦到达终点,标记为 true
    		ok[u] = true;
    		return;
    	}
    	int to;
    	to = u + a[u]; if (to >= 1 && to <= n) dfs1(to), ok[u] |= ok[to];	//回溯的时候将某条合法路径上所有点标记为通过该点可达终点
    	to = u + b[u]; if (to >= 1 && to <= n) dfs1(to), ok[u] |= ok[to];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    对于 第二遍 dfs,我们就可以开始记录方案了,中序遍历 dfs 第一次达到终点的方案,即为 字典序最小的方案,输出该字符串即可,前提是保证无环

    如果搜索过程中 搜索到了环,由于 第一遍 dfs 已经完成了 预处理 ok 数组 的操作,因此这个环也是一个 合法方案,且 字典序最小,根据题意 直接输出“Infinity!” 即可。(时间复杂度 也为:O(N)

    void dfs2(int u = 1)
    {
    	if (st[u]) {	//重复搜索到同一个位置,即搜索到了环,而且是个字典序最小的合法环
    		puts("Infinity!");
    		exit(0);
    	}
    	st[u] = true;
    	if (u == n) {	//搜到终点 由于之前完成了预处理,这个方案即为字典序最小方案,输出
    		cout << path << '\n';
    		exit(0);
    	}
    	int to;
    	to = u + a[u]; if (to >= 1 && to <= n && ok[to]) path += "a", dfs2(to);	//选择 a 方案,到达的点 to 在范围内,且通过节点 to 可达终点(ok[to] == true),就加入 a 方案,继续搜索
    	to = u + b[u]; if (to >= 1 && to <= n && ok[to]) path += "b", dfs2(to);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意

    • st 数组两次 dfs 的过程中 有着不同的用途,第一次 用于 防止重复搜索第二次 用于 搜环
    • ok[1] = true 表示 从起点 1 开始可以通过某个方案走到终点 n第一次 dfs 完以后,如果 不满足 ok[1] = true 则直接表示 没有合法方案,输出 No solution!

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    //#define map unordered_map
    //#define int long long
    const int N = 1e5 + 10;
    int a[N], b[N];
    int n;
    bool st[N], ok[N];
    string path;
    
    void dfs1(int u = 1)
    {
    	if (st[u]) return;
    	st[u] = true;
    	if (u == n) {
    		ok[u] = true;
    		return;
    	}
    	int to;
    	to = u + a[u]; if (to >= 1 && to <= n) dfs1(to), ok[u] |= ok[to];
    	to = u + b[u]; if (to >= 1 && to <= n) dfs1(to), ok[u] |= ok[to];
    }
    
    void dfs2(int u = 1)
    {
    	if (st[u]) {
    		puts("Infinity!");
    		exit(0);
    	}
    	st[u] = true;
    	if (u == n) {
    		cout << path << '\n';
    		exit(0);
    	}
    	int to;
    	to = u + a[u]; if (to >= 1 && to <= n && ok[to]) path += "a", dfs2(to);
    	to = u + b[u]; if (to >= 1 && to <= n && ok[to]) path += "b", dfs2(to);
    }
    
    signed main()
    {
    	cin >> n;
    	for (int i = 1; i <= n; ++i) {
    		scanf("%d", &a[i]);
    	}
    	for (int i = 1; i <= n; ++i) {
    		scanf("%d", &b[i]);
    	}
    	dfs1();
    	if (ok[1]) {
    		fill(st, st + n + 1, false);
    		dfs2();
    	}
    	else {
    		puts("No solution!");
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    上方的代码完全可以写成记忆化的形式,代码更加简短,不过可能没那么好理解。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int n, a[N], b[N];
    bool st[N], inf[N], ok;
    string path;
    
    bool dfs(int x)
    {
        if (x < 0 || x >= n) return false;
    
        if (st[x]) {
            inf[x] = true;
            return false;
        }
    
        if (x == n - 1) {
            return true;
        }
    
        st[x] = true;
    
        if (dfs(x + a[x])) {
            path += "a";
            if (inf[x]) ok = true;
            return true;
        }
        if (dfs(x + b[x])) {
            path += "b";
            if (inf[x]) ok = true;
            return true;
        }
        return false;
    }
    
    int main()
    {
        cin >> n;
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < n; ++i) {
            scanf("%d", &b[i]);
        }
        if (dfs(0)) {
            if (ok) {
                puts("Infinity!");
            }
            else {
                reverse(path.begin(), path.end());
                cout << path << '\n';
            }
        }
        else {
            cout << "No solution!" << '\n';
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
  • 相关阅读:
    2.【远程调用框架】Feign远程调用
    ES6如何声明一个类?类如何继承?
    卷积神经网络CNN中的卷积操作详解
    使用 C# / Unity 进行图像处理
    现代 CSS 解决方案:accent-color 强调色
    用 HarmonyOS 做一个可以手势控制的电子相册应用(ArkTS)
    Android入门第30天-Android里的Toast的使用
    VM16Pro的Win10虚拟机安装Linux子系统Kali
    STM32--EXTI外部中断
    谷歌『云开发者速查表』;清华3D人体数据集;商汤『通用视觉框架』公开课;Web3极简入门指南;高效深度学习免费书;前沿论文 | ShowMeAI资讯日报
  • 原文地址:https://blog.csdn.net/Jacob0824/article/details/126875127