【九十九】【算法分析与设计】百度之星2023部分题目,BD202311 夏日漫步,BD202314 跑步,BD202321 新材料,找下一个元素值相等的下标用map或者排序

BD202311 夏日漫步

夏日夜晚,小度看着庭院中长长的走廊,萌发出想要在上面散步的欲望,小度注意到月光透过树荫落在地砖上,并且由于树荫的遮蔽度不通,所以月光的亮度不同,为了直观地看到每个格子的亮度,小度用了一些自然数来表示它们的亮度。亮度越高则数字越大,亮度相同的数字相同。

走廊是只有一行地砖的直走廊。上面一共有 nn 个格子,每个格子都被小度给予了一个数字 a_iai 来表示它的亮度。

小度现在站在 11 号格子,想要去到 nn 号格子。小度可以正向或反向移动到相邻的格子,每次需要花费 11 的体力。

同时小度还有瞬移的能力,其可以花费 11 的体力来瞬移到与当前格子亮度相同的格子上。而且由于小度视野有限,只能瞬移到在当前格子后的第一次亮度相同的格子上。这也意味着不能反向瞬移

小度想知道,到达 nn 号格子需要花费的最小体力是多少。以此制定一个最优秀的散步方案。

格式

输入格式:

第一行一个整数 nn ,表示走廊上一共有 nn 个格子。 1\leq n \leq 2*10^51≤n≤2∗105 , 0\leq a_i \leq 1*10^60≤ai≤1∗106; 第二行 nn 个整数,为自然数 a_iai 表示第 ii 号格子的亮度。

输出格式:

一行,一个整数costcost表示花费的最小体力。

样例 1

输入:

6

0 1 2 3 1 5

复制

输出:

3

复制

1.

思路是搞一个dis数组,大小是n,一一对应每一个位置,从0号位置到达每一个位置的最近的距离,消耗最少的体力值是多少,只需要用BFS维护好这个dis值即可.

2.

BFS层次遍历,每一次走一步,有三种情况,要么是往前走一步,要么是往后走一步,要么是瞬移,这些消耗的体力值都是1.

3.

利用nextt数组存储每一个位置瞬移的下一个位置的下标,如果不存在可以瞬移的位置就填-1.

4.

如何维护next数组,有两种办法,第一种办法利用map存储值和下标的映射,值与下标的映射始终保证是最后一次出现的下标值.

遍历arr数组,对于i位置的元素值,看一下map里面有没有这个值,如果有,维护这个map中的nextt值.如果没有什么都不做,然后将i位置添加到map中.

5.

第二种维护nextt的办法就是排序,node结构体绑定元素值和下标,然后按照元素值排序,元素值相等按照下标排序.

map维护nextt

 
#include<bits/stdc++.h> // 包含所有标准库

using namespace std;
#define int long long // 定义int为long long,便于处理大数
#define endl '\n' // 将换行符定义为'\n'

int n; // 定义格子数
vector<int> arr; // 定义亮度数组
vector<int> next_; // 定义下一个相同亮度格子的位置数组
map<int, int> prev_; // 定义一个映射,用于存储上一次出现相同亮度的格子的位置
int ret = 0; // 定义返回值
vector<int> dis; // 定义距离数组

void bfs() { // 广度优先搜索函数
    queue<int> q; // 定义队列用于BFS
    dis[1] = 0; // 初始化起点的距离为0
    q.push(1); // 将起点入队
    while (!q.empty()) { // 当队列不为空时
        int top = q.front(); // 获取队首元素
        q.pop(); // 弹出队首元素
        if (top == 0) continue; // 如果当前格子为0,跳过(不可能发生,因为起点为1)

        if (dis[top + 1] == -1) { // 如果可以向右移动且未访问
            dis[top + 1] = dis[top] + 1; // 更新右侧格子的距离
            q.push(top + 1); // 将右侧格子入队
            if (top + 1 == n) return; // 如果到达终点,结束函数
        }
        if (top - 1 >= 0 && dis[top - 1] == -1) { // 如果可以向左移动且未访问
            dis[top - 1] = dis[top] + 1; // 更新左侧格子的距离
            q.push(top - 1); // 将左侧格子入队
        }

        if (next_[top] != -1 && dis[next_[top]] == -1) { // 如果可以瞬移且未访问
            dis[next_[top]] = dis[top] + 1; // 更新瞬移目标格子的距离
            q.push(next_[top]); // 将瞬移目标格子入队
            if (next_[top] == n) return; // 如果到达终点,结束函数
        }
    }
}

void solve() { // 解决问题的函数
    dis.assign(n + 1, -1); // 初始化距离数组,所有元素设为-1
    ret = 0; // 重置返回值
    next_.assign(n + 1, -1); // 初始化下一个相同亮度格子的位置数组
    prev_.clear(); // 清空映射
    for (int i = 1; i <= n; i++) { // 遍历每个格子
        if (prev_.count(arr[i])) { // 如果当前亮度值之前出现过
            next_[prev_[arr[i]]] = i; // 更新上一次出现相同亮度格子的位置的下一个相同亮度格子的位置
        }
        prev_[arr[i]] = i; // 更新当前亮度值出现的位置
    }

    bfs(); // 执行广度优先搜索
    cout << dis[n] << endl; // 输出到达终点的最小体力
}

signed main() { // 主函数
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 快速输入输出
    cin >> n; // 输入格子数
    arr.assign(n + 1, -1); // 初始化亮度数组
    for (int i = 1; i <= n; i++) { // 输入每个格子的亮度
        cin >> arr[i];
    }
    solve(); // 调用solve函数解决问题
    return 0; // 返回0
}

排序维护nextt

 
#include<bits/stdc++.h> // 包含所有标准库
using namespace std;

#define DEBUG // 定义DEBUG标志
#ifdef DEBUG
#define BUG(code) do{code}while(0); // 如果定义了DEBUG,则执行code块
#else 
#define BUG(code) do{}while(0); // 如果没有定义DEBUG,则不执行code块
#endif

#define int long long // 将int定义为long long,便于处理大数
#define endl '\n' // 将换行符定义为'\n'
#define FAST() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 快速输入输出宏

#define p pair<int, int> // 定义p为pair<int, int>类型
#define ff first // 定义ff为first
#define ss second // 定义ss为second
#define pb push_back // 定义pb为push_back
#define ppb pop_back() // 定义ppb为pop_back()
#define ltu(i, a, b) for(int i = a; i <= b; i++) // 定义ltu为从a到b的for循环
#define utl(i, a, b) for(int i = a; i >= b; i--) // 定义utl为从a到b的倒序for循环
#define tests() int t; cin >> t; while(t--) // 定义tests为测试用例循环

int n; // 定义变量n
vector<int> arr; // 定义亮度数组
vector<int> dis; // 定义距离数组
vector<int> nextt; // 定义下一个相同亮度格子的位置数组

struct node { // 定义结构体node
    int val; // 亮度值
    int index; // 下标
};

vector<node> arr1; // 定义node结构体的数组

void bfs() { // 广度优先搜索函数
    queue<int> q; // 定义队列用于BFS
    dis[0] = 0; // 初始化起点的距离为0
    q.push(0); // 将起点入队
    while (!q.empty()) { // 当队列不为空时
        auto top = q.front(); // 获取队首元素
        q.pop(); // 弹出队首元素

        if (top == n - 1) { // 如果到达终点
            cout << dis[top] << endl; // 输出到达终点的最小体力
            return; // 结束函数
        }

        if (top - 1 >= 0 && dis[top - 1] == -1) { // 如果可以向左移动且未访问
            dis[top - 1] = dis[top] + 1; // 更新左侧格子的距离
            q.push(top - 1); // 将左侧格子入队
        }
        if (top + 1 < n && dis[top + 1] == -1) { // 如果可以向右移动且未访问
            dis[top + 1] = dis[top] + 1; // 更新右侧格子的距离
            q.push(top + 1); // 将右侧格子入队
        }
        if (nextt[top] != -1 && dis[nextt[top]] == -1) { // 如果可以瞬移且未访问
            dis[nextt[top]] = dis[top] + 1; // 更新瞬移目标格子的距离
            q.push(nextt[top]); // 将瞬移目标格子入队
        }
    }
}

void solve() { // 解决问题的函数
    dis.assign(n, -1); // 初始化距离数组,所有元素设为-1
    nextt.assign(n, -1); // 初始化下一个相同亮度格子的位置数组
    sort(arr1.begin(), arr1.end(), [](const node &a, const node &b) { // 对arr1按亮度和下标排序
        if (a.val == b.val) {
            return a.index < b.index; // 如果亮度相同,按下标排序
        } else {
            return a.val < b.val; // 否则按亮度排序
        }
    });
    ltu(i, 0, n - 2) { // 遍历arr1
        if (arr1[i].val == arr1[i + 1].val) { // 如果相邻两个格子的亮度相同
            nextt[arr1[i].index] = arr1[i + 1].index; // 更新nextt数组
        }
    }
    bfs(); // 执行广度优先搜索
    // BUG(
    //     ltu(i, 0, n - 1) {
    //         cout << dis[i] << " ";
    //     }
    //     cout << endl;
    // );
}

signed main() { // 主函数
    FAST(); // 快速输入输出
    cin >> n; // 输入格子数
    arr.assign(n, 0); // 初始化亮度数组
    arr1.assign(n, node()); // 初始化node结构体数组
    ltu(i, 0, n - 1) { // 输入每个格子的亮度
        cin >> arr[i];
        arr1[i].val = arr[i]; // 将亮度赋值给arr1
        arr1[i].index = i; // 记录下标
    }
    solve(); // 调用solve函数解决问题
    return 0; // 返回0
}

BD202314 跑步

小度每天早上要和小猫一起跑步。小猫的位置数值越小表示越在后面,速度越小表示越慢,它们都向一个方向跑。

小猫比较喜欢一起跑,所以当速度更快的小猫遇见速度慢的小猫时,它就会放慢速度,变成一组一起跑。注意,初始位置相同的小猫直接组成一组。

请问最终不再有追赶上的情况时,最多一组有多少只小猫?

格式

输入格式:

第一行输入的是整数 nn,1 \le n \le 10^51≤n≤105; 接下来的nn行分别包含小猫的初始位置 pp 和速度 vv ,1 \le p,v \le 10^8 1≤p,v≤108。

输出格式:

一行,一个数字,表示最多有多少小猫在一组。

样例 1

输入:

5

6 1

1 1

3 2

1 2

2 3

复制

输出:

3

复制

备注

样例解释:位置速度为(3,2),(2,3)的小猫会追上(6,1)的小猫,而位置速度为(1,2)的小猫则会和(1,1)一起,所以最多三只小猫一组。

1.

对于一个小猫,如果他的速度不会变小,可以一直跑下去,说明它前面的小猫的速度全部都大于它.

对于一个小猫,如果他的速度会变小,说明前面有速度小于它的小猫.

小猫的相对位置不会改变,i位置的小猫速度是v,那么i后面的小猫要么和小猫一起,要么在i位置小猫后面.

i前面的小猫要么和小猫一起,要么在i位置前面.

2.

按照位置大小进行排序,从小到大,如果位置相等按照速度进行排序,从小到大.

3.

从后往前遍历,最后一个小猫可以一直跑下去,他会带多少个小猫一起跑?速度大于他的小猫都会带上.速度小于等于他的小猫,一定追不上.

4.

记录每一个组的小猫数量的最大值.

 
#include<bits/stdc++.h> // 包含所有标准库
using namespace std;
#define int long long // 定义int为long long,便于处理大数
#define endl '\n' // 将换行符定义为'\n'

int n; // 定义小猫数量
struct node { // 定义结构体node
    int p, v; // p表示位置,v表示速度
};
vector<node> arr; // 定义node结构体的数组
int ret = 0; // 定义返回值

void solve() { // 解决问题的函数
    sort(arr.begin(), arr.end(), [](const node& a, const node& b) { // 对arr排序
        if (a.p != b.p) return a.p < b.p; // 按位置升序排序
        else return a.v > b.v; // 如果位置相同,按速度降序排序
    });
    int count = 0; // 计数当前组的小猫数量
    int minv = LLONG_MAX; // 定义当前组的小猫最小速度

    for (int i = arr.size() - 1; i >= 0; i--) { // 从后往前遍历
        if (arr[i].v <= minv) { // 如果当前小猫的速度小于等于当前组的最小速度
            minv = arr[i].v; // 更新最小速度
            count = 0; // 重置计数
        }
        count++; // 计数加一
        ret = max(ret, count); // 更新最大组的小猫数量
    }

    cout << ret << endl; // 输出结果
}

signed main() { // 主函数
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 快速输入输出
    cin >> n; // 输入小猫数量
    arr.assign(n, node()); // 初始化node结构体数组
    for (int i = 0; i < n; i++) { // 输入每只小猫的位置和速度
        cin >> arr[i].p >> arr[i].v;
    }
    solve(); // 调用solve函数解决问题
    return 0; // 返回0
}

BD202321 新材料

小度最近在研究超导材料,他合成了 NN 个新材料排成一排,而每个材料根据基于流程会设置一个编号 PP,表示种类。 小度发现相同种类的材料在距离小于等于 KK 的情况下会发生发应。 小度想知道哪些种类的材料会发生反应,输出这些种类 PP 的异或值。

格式

输入格式:

第一行,两个整数 N, KNK(1 \le K \le N \le 500001≤KN≤50000); 接下来 NN 行,每行一个整数,即该位置材料的的种类编号 PP( 0 \le P \le 10000000≤P≤1000000)。

输出格式:

一行,一个整数,会发生反应的种类 PP 的异或值。

样例 1

输入:

5 3

1

2

4

1

4

复制

输出:

5

复制

备注

材料1,4会发生反应,异或值为5。

1.

同样是维护nextt数组,可以用map维护也可以用排序维护.

map维护nextt

 
#include<bits/stdc++.h> // 包含所有标准库
using namespace std;

// #define PrDEBUG // 定义调试宏
#ifdef PrDEBUG
#define de(x) cerr<<"Line "<<__LINE__<<": "<<#x<<" = "<<x<<endl // 如果定义了PrDEBUG,则输出调试信息
#else
#define de(x) // 如果没有定义PrDEBUG,则不输出调试信息
#endif

#define int long long // 定义int为long long,便于处理大数
#define endl '\n' // 将换行符定义为'\n'
#define FAST() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) // 快速输入输出宏
#define p pair<int, int> // 定义p为pair<int, int>类型
#define ff first // 定义ff为first
#define ss second // 定义ss为second
#define pb push_back // 定义pb为push_back
#define ppb pop_back // 定义ppb为pop_back
#define ltu(i, a, b) for(int i = a; i <= b; ++i) // 定义ltu为从a到b的for循环
#define utl(i, a, b) for(int i = a; i >= b; --i) // 定义utl为从a到b的倒序for循环
#define tests() int t; cin >> t; while(t--) // 定义tests为测试用例循环

int N, K; // 定义材料数量N和最大距离K
vector<int> arr; // 定义材料种类编号数组
vector<int> nextt; // 定义下一个相同种类材料的距离数组
map<int, int> prevv; // 定义一个映射,用于存储上一次出现相同种类材料的位置
int ret = 0; // 定义返回值

void solve() { // 解决问题的函数
    ret = 0; // 初始化返回值
    nextt.assign(1000001, LLONG_MAX); // 初始化nextt数组,大小为1000001,所有元素设为LLONG_MAX
    prevv.clear(); // 清空映射
    de(arr[5]); // 调试信息

    ltu(i, 1, N) { // 遍历每个材料
        if(prevv.count(arr[i])) { // 如果当前材料的种类之前出现过
            de(i); // 调试信息
            nextt[arr[i]] = min(nextt[arr[i]], i - prevv[arr[i]]); // 更新该种类材料的最小距离
        }
        prevv[arr[i]] = i; // 更新当前种类材料的位置
    }

    ltu(i, 1, 1000000) { // 遍历所有可能的种类
        if(nextt[i] <= K) ret ^= i; // 如果该种类材料的最小距离小于等于K,则更新返回值
    }

    cout << ret << endl; // 输出结果
}

signed main() { // 主函数
    FAST(); // 快速输入输出
    cin >> N >> K; // 输入材料数量和最大距离
    arr.assign(N + 1, 0); // 初始化材料种类编号数组
    ltu(i, 1, N) { // 输入每个材料的种类编号
        cin >> arr[i];
    }
    solve(); // 调用solve函数解决问题
    return 0; // 返回0
}

排序维护nextt

 
#include<bits/stdc++.h> // 包含所有标准库
using namespace std;

#define PrDEBUG // 定义调试宏
#ifdef PrDEBUG
#define de(x) cerr<<"Line "<<__LINE__<<": "<<#x<<" = "<<(x)<<endl // 如果定义了PrDEBUG,则输出调试信息
#else
#define de(x) // 如果没有定义PrDEBUG,则不输出调试信息
#endif

#define int long long // 定义int为long long,便于处理大数
#define endl '\n' // 将换行符定义为'\n'
#define FAST() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) // 快速输入输出宏

#define p pair<int, int> // 定义p为pair<int, int>类型
#define ff first // 定义ff为first
#define ss second // 定义ss为second
#define pb push_back // 定义pb为push_back
#define ppb pop_back // 定义ppb为pop_back
#define ltu(i, a, b) for(int i = a; i <= b; ++i) // 定义ltu为从a到b的for循环
#define utl(i, a, b) for(int i = a; i >= b; --i) // 定义utl为从a到b的倒序for循环
#define tests() int t; cin >> t; while(t--) // 定义tests为测试用例循环

int N, K; // 定义材料数量N和最大距离K
struct node { // 定义结构体node
    int bianhao; // 材料编号
    int indexx; // 材料位置索引
};
vector<node> arr; // 定义node结构体的数组
map<int, bool> mapp; // 定义一个映射,用于存储已经处理过的材料种类
int ret = 0; // 定义返回值

void solve() { // 解决问题的函数
    ret = 0; // 初始化返回值
    mapp.clear(); // 清空映射

    // 对arr进行排序,首先按编号升序排序,如果编号相同按索引升序排序
    sort(arr.begin(), arr.end(), [](const node& a, const node& b) {
        if (a.bianhao == b.bianhao) {
            return a.indexx < b.indexx;
        } else {
            return a.bianhao < b.bianhao;
        }
    });

    // 遍历所有材料,检查相邻的同种材料是否在最大距离K内
    ltu(i, 1, N - 1) {
        if (arr[i].bianhao == arr[i + 1].bianhao && !mapp.count(arr[i].bianhao)) { // 如果相邻材料编号相同且该编号未处理过
            if (arr[i+1].indexx - arr[i].indexx <= K) { // 检查相邻材料是否在最大距离K内
                mapp[arr[i].bianhao] = true; // 标记该编号已经处理过
                ret ^= arr[i].bianhao; // 将该编号的值加入结果异或值
            }
        }
    }
    cout << ret << endl; // 输出结果
}

signed main() { // 主函数
    FAST(); // 快速输入输出
    cin >> N >> K; // 输入材料数量和最大距离
    arr.assign(N + 1, node()); // 初始化node结构体数组
    ltu(i, 1, N) { // 输入每个材料的编号
        cin >> arr[i].bianhao;
        arr[i].indexx = i; // 记录材料的位置索引
    }
    solve(); // 调用solve函数解决问题
    return 0; // 返回0
}

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖精七七_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值