IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【蓝桥杯冲刺 day12】题目全解析 -> 正文阅读

[数据结构与算法]【蓝桥杯冲刺 day12】题目全解析

Hello大家好,我是秋刀鱼,今天又来给大家更新蓝桥杯真题题解了。

今天的题目不算太难,不过需要注意的细节还是很多,无论是日常的刷题还是比赛编写代码时一定要注意细节部分,不要因为疏忽导致失分。

如果觉得作者写的还不错的朋友可以一键三连支持一下! ?( ′・?・` )比心

img


往期蓝桥杯算法解析:

【蓝桥杯冲刺 day10】题目全解析 — 难题突破

【蓝桥杯冲刺 day8】题目全解析 —附上LeetCode 每日一题

【蓝桥杯冲刺 day7】 题目全解析 — 附上LeetCode周赛 银联-03. 理财产品

【蓝桥杯冲刺 day4】题目全解析 — 每日刷题解析

【蓝桥杯】算法训练 无聊的逗

【蓝桥杯】算法提高 打包

【蓝桥杯】算法提高 学生节

打水问题

题目传送门

题目描述

N个人要打水,有M个水龙头,第i个人打水所需时间为Ti,请安排一个合理的方案使得所有人的等待时间之和尽量小。

提示
一种最佳打水方案是,将N个人按照Ti从小到大的顺序依次分配到M个龙头打水。
例如样例中,Ti从小到大排序为1,2,3,4,5,6,7,将他们依次分配到3个龙头,则去龙头一打水的为1,4,7;去龙头二打水的为2,5;去第三个龙头打水的为3,6。
第一个龙头打水的人总等待时间 = 0 + 1 + (1 + 4) = 6
第二个龙头打水的人总等待时间 = 0 + 2 = 2
第三个龙头打水的人总等待时间 = 0 + 3 = 3
所以总的等待时间 = 6 + 2 + 3 = 11

输入

第一行两个正整数N M 接下来一行N个正整数Ti。
N,M< =1000,Ti< =1000

输出

最小的等待时间之和。(不需要输出具体的安排方案)

样例输入复制

7 3
3 6 1 4 2 5 7

样例输出复制

11

题目解析

核心思路是贪心,确实还是第一次见有题目直接把核心的思路写在题目里面 😆

代码部分就是简单地实现题目中的核心思路,不过多的赘述。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
#define M 1001
int main()
{
    int n, k;
    int time[M];
    for (int i = 1; i <= n; ++i) {
        cin >> time[i - 1];
    }
    // 按照时间顺序排序
    sort(time, time + n);
    int ans = 0;
    for (int i = k; i <= n; ++i) {
        ans += time[i] - time[i - k];
    }
    cout << ans;
    return 0;
}

思考1:为什么这样分配水龙头等待的时间之和会是最短的呢?

因为基于贪心的思想,为了使总等待时间最少,那么水龙头会优先分配给打水所需时间最少的人,也就是说,刚开始时,水龙头一定会分给所需时间最少的 K K K 个人。如果将顺序数组按顺序 K K K 个人当做一组,那么第二组:也就是排序后索引为 [ K , 2 ? K ) [K,2*K) [K,2?K) 的这些人一定是下一批分配水头的,只有第二组人均被分配水龙头后,第三组的人才能被分配水龙头。

那么现在问题来了,第一组第一个人最先完成,优先给第二组的哪一个人分配水龙头呢?

答案是,第二组的任意一个人都可以。假设第 i i i 个人打水时间为 t i m e i time_i timei? ,那么无论怎么分配水龙头,第二组所有成员等待的时间和一定是: ∑ i = 0 K ? 1 t i m e i \displaystyle \sum^{K-1}_{i = 0} time_i i=0K?1?timei? ,无论如何分配都是这个值,所以第二组任意一个人都可以得到空闲的水龙头。


夺宝奇兵

题目传送门

题目描述

在一座山上,有很多很多珠宝,它们散落在山底通往山顶的每条道路上,不同道路上的珠宝的数目也各不相同.下图为一张藏宝地图:

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

”夺宝奇兵”从山下出发,到达山顶,如何选路才能得到最多的珠宝呢?在上图所示例子中,按照5-> 7-> 8-> 3-> 7的顺序,将得到最大值30

输入

第一行正整数N(100> =N> 1),表示山的高度
接下来有N行非负整数,第i行有i个整数(1< =i< =N),表示山的第i层上从左到右每条路上的珠宝数目

输出

一个整数,表示从山底到山顶的所能得到的珠宝的最大数目.

样例输入复制

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

样例输出复制

30

写在前面

这道题或多或少优点坑了😢

根本没有规定移动路径,只是简简单单提了句从山底到山顶,如果是我我完全能够拿完全部的珠宝,当然这样题目就太简单了。在很多次wa后发现这题只能往上走或左上行走,所有友友们要注意一下。

题目解析

一道基础的动态规划题目。动态规划作为蓝桥杯的必考知识点,十分重要!如果还没有学习过动态规划一定要预先学习!

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 存放 第 i i i 行,第 j j j 个位置时,所获得的珠宝最大数目。虽然说题目给定是从山底到山顶,但是也同样可以反推:从山顶到山底收获宝藏的最大值,移动时只能向下或右下方向。

对于 d p [ i ] [ j ] dp[i][j] dp[i][j] 的值有两种可能:要么带上 上方的宝藏下来,要么带上左上方的宝藏下来,这两种可能会发生,因此我们还需要找到两种可能的最大值就是我们所需要的值,dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])

#include <bits/stdc++.h>
using namespace std;
#define M 101
int main()
{
    // 边界不保留元素,防止出界
    int dp[M+1][M+1]{ {0} };
    int n;
    cin >> n;
    int ans = -11111;
    for (int i = n - 1; i >= 0; --i) {
        for (int j = 1; j <= n - i; ++j) {
            cin >> dp[i][j];
            if (i != n - 1) {
                dp[i][j] += max(dp[i + 1][j - 1], dp[i + 1][j]);
            }
            if (i == 0) {
                ans = max(ans, dp[i][j]);
            }
        }
    }
    cout << ans;
    return 0;
}

同样的动态规划问题在很多情况下都能压缩 d p dp dp 数组,本题因为状态只来自于上一行的数据,因此只需要记录上一行的左上角数据于上方数据,就能使用一维数组实现:

#include<bits/stdc++.h>
using namespace std;
#define M 101
int main()
{
    int dp[M+1]{ 0 };
    int n;
    cin >> n;
    int ans = -11111;
    int leftTop;
    for (int i = n - 1; i >= 0; --i) {
        // 保存左上角值
        leftTop = 0;
        for (int j = 1; j <= n - i; ++j) {
            // 保留顶部的值
            int top = dp[j];
            cin >> dp[j];
            if (i != n - 1) {
                dp[j] += max(top, leftTop);
                leftTop = top;
            }
            // cout << i << "  " << j <<"  " << dp[j] << endl;
            if (i == 0) {
                ans = max(ans, dp[j]);
            }
        }
    }
    cout << ans;
    return 0;
}


调手表

题目传送门

题目描述

小明买了块高端大气上档次的电子手表,他正准备调时间呢。

在 M78 星云,时间的计量单位和地球上不同,M78 星云的一个小时有 n 分钟。

大家都知道,手表只有一个按钮可以把当前的数加一。在调分钟的时候,如果当前显示的数是 0 ,那么按一下按钮就会变成 1,再按一次变成 2 。如果当前的数是 n - 1,按一次后会变成 0。

作为强迫症患者,小明一定要把手表的时间调对。如果手表上的时间比当前时间多 1,则要按 n - 1次加一按钮才能调回正确时间。

小明想,如果手表可以再添加一个按钮,表示把当前的数加 k 该多好啊…

他想知道,如果有了这个 +k 按钮,按照最优策略按键,从任意一个分钟数调到另外任意一个分钟数最多要按多少次。

注意,按 +k 按钮时,如果加 k 后数字超过 n-1,则会对 n 取模。

比如,n=10, k=6 的时候,假设当前时间是 0,连按 2 次 +k 按钮,则调为 2。

输入描述

一行两个整数 n, k (0 < k < n < 10^5),意义如题。

输出描述

输出一行一个整数,表示按照最优策略按键,从一个时间调到另一个时间最多要按多少次。

输入输出样例

示例

输入

5 3

输出

2

样例解释

如果时间正确则按 0 次。否则要按的次数和操作系列之间的关系如下:

1:+1

2:+1, +1

3:+3

4:+3, +1

解题思路

多重背包思路(超时)

本题的数据相当刁钻,数据量高达 1 0 5 10^5 105 ,也就是说一般思路的多重背包问题并不适合作为本题的解决方法,因为背包问题的时间复杂度通常会达到 O ( N 2 ) O(N^2) O(N2)

不过多重背包的思路仍然可以学习:

多重背包问题通常是给定一个重量weight,给定很多商品的价值value[]、重量weight[],并且每一个商品都能被无限次地购买,问该背包重量下最大购买的商品价值。

  • 因为每一次能够跳跃 1 1 1 K K K 次,且可以进行无数次地跳跃,可以将其当做商品价值
  • 每一个时刻距离 0 时 刻 0时刻 0 位置可以当做给定的背包重量。

这样就能将问题转换为多重背包问题,因为没有AC,所以就不附上代码了。

宽度优先遍历BFS

既然多重背包没办法解决,可以转变下思路,既然需要找到最大次数,使用BFS算法是一定能够求解的,且寻找的是最短的最大次数,因此BFS算法高于DFS算法,于是可以有如下的尝试:

#include<bits/stdc++.h>
using namespace std;
#define M 10010
#define INTMIN -2e6
int ans[M];
void bfs(int n, int k) {
    queue<int>qu;
    qu.push(0);
    int time = 0;
    int t = 0;
    while (qu.size()) {
        int len = qu.size();
        for (int i = 0; i < len; ++i) {
            int idx = qu.front();
            qu.pop();

            if (ans[idx] <0 ) {
                ++t;
                ans[idx] = time;
            }
            if (t == n) {
                return;
            }
            int nx1 = (idx + n + 1) % n;
            int nx2 = (idx + k + n) % n;
            if (ans[nx1] < 0) {
                qu.push(nx1);
            }
            if (ans[nx2] < 0) {
                qu.push(nx2);
            }
        }
        ++time;
    }
}
int main()
{
    int n, k;
    memset(ans, INTMIN, sizeof(ans));
    cin >> n >> k;
    bfs(n, k);
    int m = -1;
    for (int i = 0; i < n; ++i) {
        m = max(m, ans[i]);
    }
    cout << m;
    return 0;
}

结果还是没能成功AC,原因主要有如下几点:

  • 数据量过大,重复遍历次数过多
  • 最后一次循环遍历数据量巨大,浪费大量时间

优化思路如下:

  1. 对于 +1 这种来说,电子表的时间从 n ? 1 ? ? > 0 n-1~ -> 0 n?1??>0 调整次数为 n n n ,但是考虑到从 n ? k n-k n?k 通过 +K 操作同样到 0 调整总次数为 n ? k + 1 n-k+1 n?k+1 ,而 k > 0 k>0 k>0 ,因此: n > = n ? k + 1 n>=n-k+1 n>=n?k+1 ,所以当时间超过n-1时跳转为0的情况均不需要考虑+1,实现剪枝操作
  2. 在上面的代码中,判断退出放在了取出队列元素的操作之后,也就是说 w h i l e while while 会多执行一轮循环才会返回结果。只需要将判断运算放在取出队列元素的操作之前,就可以省去不必要的一轮 w h i l e while while 循环
#include<bits/stdc++.h>
using namespace std;
int ans[100001];
struct node {
	int i;
	int t;
	node(int i, int t) {
		this->i = i;
		this->t = t ;
	}
};
int main()
{
	int n, k,bk;
	cin >> n >> k;
	queue<node>qu;
	qu.push(node(0, 0));
	while (!qu.empty())
	{
		node cur = qu.front();
		qu.pop();
		int idx = cur.i;
		int t = cur.t;
		++t;
		int nx1 = idx + 1;
		int nx2 = (idx + k) % n;
        // 先判断后在推入元素 同时判断 nx1是否大于n-1进行剪枝
		if (nx1 < n && !ans[nx1]) {
			ans[nx1] = t;
			qu.push(node(nx1, t));
		}
		if (!ans[nx2]) {
			ans[nx2] = t;
			qu.push(node(nx2, t));
		}
	}
	bk = 0;
	for (int i = 1; i < n; i++)
	{
		bk = max(ans[i], bk);
	}
	cout << bk;
	return 0;
}

写在最后

代码、论述中有任何问题,欢迎大家指出,同时如果有任何疑问,也能够在评论区中留言,大家共同讨论共同进步!

微笑
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 21:17:10  更:2022-03-21 21:18:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 2:08:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码