2020年牛客算法入门课练习赛2

本文介绍了两道与牛市遗迹相关的编程题目,第一题涉及攀登天梯的最短步数问题,采用动态规划解决;第二题描述了流星雨下家族的生存策略,通过广度优先搜索找到最短逃生时间;第三题讲述了迁徙过程中渡河的最短时间安排,使用动态规划求解。这些题目展示了在复杂环境下寻找最优路径的算法应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

古老的牛市,遗迹的天梯

链接:https://ac.nowcoder.com/acm/contest/12145/A
来源:牛客网

题目描述
牛市,一个拥有悠久历史的城市,2333年考古学家在牛市发现了一个神秘的遗迹,这些勇敢而智慧的古队员准备进入这个遗迹,但要进入这个遗迹就需要通过一段天梯。而登上天梯必须要按照它要求的方法,否则就无法登上。它要求的方法为:

1.可以直接登上比当前位置高1个单位高度的天梯。

2.可以从当前阶梯往下退一级天梯(第一级天梯除外)。

3.在连续退k步后,跳跃一次,跳跃的高度不超过2^k。比如说你现在位于第i级天梯,且之前从第i+k级天梯退下来,此时你可以跳到高度不超过(当前高度+ 2^k)的任何一级天梯。每一次跳跃只算一次移动哦!

开始的时候考古小队在第一级天梯。请你计算出最少的移动步数以登上最高一级天梯。

为何考古搞得跟游戏历险一样?牛市一定是一个魔性的城市!

输入描述:
第1行:一个整数N,表示天梯级数。

第2行:N个整数,依次为每层天梯梯的高度,保证递增。

输出描述:
一个整数,如果能登上天梯,输出最小步数,否则输出-1。

示例1
输入
复制
5
0 1 2 3 6
输出
复制
7
说明
1 \leq N \leq 2001≤N≤200。 每级阶梯高度不超过2^{31}-12
31
−1。

这道题正解是dp,但是我一看最短的距离,不是最短路吗,然后最短路最重要的是什么,是合理的建图方式,建图决定了一切

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e7;
const int M = N;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int h[N], e[M], ne[M], w[M], idx;
int a[N];
int n;
bool st[N];
int dist[N];

void add(int a, int b, int c){
	e[idx] = b, ne[idx] = h[a],w[idx] = c, h[a] = idx ++;
}

void dijkstra(){
	priority_queue<PII, vector<PII>, greater<PII>> heap;
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	heap.push({0, 1});
	
	while(heap.size()){
		auto t = heap.top();
		heap.pop();
		
		int ver = t.second, distance = t.first;
	//	cout << ver << "----" << distance << endl;
		
		if (st[ver])   continue;
		st[ver] = true;
		
		for (int i = h[ver]; ~i; i = ne[i]){
			int j = e[i];
			
			if (dist[j] > dist[ver] + w[i]){
				dist[j] = dist[ver] + w[i];
				heap.push({dist[j], j});
			}
		}
	}
}

signed main(){
	memset(h, -1, sizeof h);
	ios;
	cin >> n;
	
	for (int i = 1; i <= n; i ++){
		cin >> a[i];
		if (i >= 2){
			if (a[i] - a[i - 1] == 1){
				add(i - 1, i, 1);
			}
			add(i, i - 1, 1);
		}
	}
	
	for (int i = n; i >= 2; i --){
		for (int j = i - 1; j >= 1; j --){
			int temp = i - j;
			int ans = 1;
			for (int k = 1; k <= temp; k ++){
				ans *= 2;
				if (ans > 1e13){
					ans = 1e13;
					break;
				}
			}
			
			for (int k = j + 1; k <= n; k ++){
				if (a[k] - a[j] <= ans){
					add(i, k, temp + 1);
				}
			}
			
		}
	}
	
	dijkstra();
	
	if (dist[n] == 0x3f3f3f3f3f3f3f3f)   dist[n] = -1;
	cout << dist[n] << endl;
	
	return 0;
}

几乎毁灭牛市的流星雨

链接:https://ac.nowcoder.com/acm/contest/12145/B
来源:牛客网

题目描述
考古队员发现,牛市之所以会有那么多古老遗迹,是因为牛市曾经遭遇过一场几乎毁灭了他的流星雨,那场流星雨中流星体积很大,无法在撞击到地面前燃烧完,所以对牛市几乎造成了毁灭性的打击,但是,我们牛市的先民也是很厉害的,他们对于流星雨的预报虽然没有提前太多的时间但是详细到了每颗流星坠落的位置,所以虽然牛市在那一场流星雨之后满目疮痍,但是牛市的百姓大多都存活了下来。因为自然环境受到了巨大的破坏,他们记录下了这段历史搬到其他的地方繁衍生息,很多代人之后,牛市的自然环境有了恢复,他们后代中的一些人又搬了回来,逐渐建成了现在的牛市。这段历史被遗忘被尘封多年,但终于还是没有被遗忘……
根据遗迹中某个记载,当时先民们预报了一共有M颗流星(1 \leq M \leq 50,000)(1≤M≤50,000)会坠落,他们将牛市划分成网格,他们预报其中第i颗流星会在时刻T_i(0 \leq T_i \leq 1,000)T
i

(0≤T
i

≤1,000)砸在坐标为(X_i, Y_i)(0 <= X_i <= 300,0 <= Y_i <= 300)(X
i

,Y
i

)(0<=X
i

<=300,0<=Y
i

<=300)的格子里。流星的力量会将它所在的格子,以及周围4个相邻的格子都化为焦土,在整个流星雨结束之前,这些格子都将无法行走站立。
现在有一个家族,在0时刻在0行0列的格子里,因为道路和建筑的原因,他们只能平行于坐标轴行动,每1个时刻,他们能移动到相邻的4个格子中的任意一个,当然这个格子要没有被撞击烧焦才行。(也就是说如果一个格子在时刻t被流星撞击或烧焦,那么他们只能在t之前的时刻在这个格子里出现。)
请你计算,这个家族是否在这场流星雨中幸存(移动到了一个没有被撞击或者烧焦的格子里一直待到流星雨结束),如果幸存,他们最少要花多少时间才移动到安全的格子里。
注:虽然流星只是砸在x,y坐标300的范围内,但是我们可以认为牛市在整个第一象限之内,即移动过程中x和y都可以超出300(但不能为负)。

输入描述:
第1行: 1个正整数:M

第2…M+1行: 第i+1行为3个用空格隔开的整数:X_i, Y_i,T_iX
i

,Y
i

,T
i

输出描述:
输出1个整数,即这个家族逃生所花的最少时间。如果这个家族无论如何都无法在流星雨中存活下来,输出-1
示例1
输入
复制
4
0 0 2
2 1 2
1 1 2
0 3 5
输出
复制
5
说明
这个家族最后待在了(0,5),(1,4)或者(2,3)这三个格子其中之一,他们都可以在5单位时间到达

每步的长度是1,最先到达最近的安全的点,可以bfs, 需要注意的点是一个点可能被炸很多次,所以取被炸的最小值就可以了

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 310;
const int M = N * N;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int s[N + 10][N + 10];
int m;
set<PII> ss;
int dist[N][N];
bool st[N][N];

struct Node{
	int x, y;
	int t;
};

int bfs(){
	queue<Node> q;
	q.push({1, 1, 0});
	if (s[1][1] == 0){
		return -1;
	}
	memset(dist, 0x3f, sizeof dist);
	dist[1][1] = 0;
	st[1][1] = true;
	
	while(q.size()){
		auto pp = q.front();
		q.pop();
		
		int x = pp.x, y = pp.y, t = pp.t;
		
	//	cout << x << "---" << y << endl;
		for (int i = 0; i < 4; i ++){
			int xx = x + dx[i], yy = y + dy[i];
			if (xx < 1 || yy < 1)    continue;
			if (xx > 301 || yy > 301){
				return dist[x][y] + 1;
			}
 			
			if (st[xx][yy])   continue;
			st[xx][yy] = true;
			
			int tt = t + 1;
			if (ss.count({xx, yy})){
				return dist[x][y] + 1;
			}
			
			if (s[xx][yy] > tt){
				dist[xx][yy] = dist[x][y] + 1;
				q.push({xx, yy, tt});
			}
		}
	}
	
	return -1;
}

signed main(){
	ios;
	cin >> m;
	
	for (int i = 1; i <= N; i ++){
		for (int j = 1; j <= N; j ++){
			s[i][j] = 2000;		
		}
	}
	
	for (int i = 1; i <= m; i ++){
		int x, y, t;
	
		cin >> x >> y >> t;
			x ++;
		y ++;
		s[x][y] = min(s[x][y], t);
		for (int j = 0; j < 4; j ++){
			int xx = x + dx[j], yy = y + dy[j];
			if (xx < 1 || y < 1)   continue;
			s[xx][yy] = min(s[xx][yy], t);
		}
		//cout << x << "---" << y << "---" << t << endl;
	}
	
	for (int i = 1; i <= N; i ++){
		for (int j = 1; j <= N; j ++){
			if (s[i][j] == 2000){
				ss.insert({i, j});
			//	cout << i << "---" << j << endl;
			}
		}
	}
	
	cout << bfs() << endl;
	
	return 0;
}

迁徙过程中的河流
链接:https://ac.nowcoder.com/acm/contest/12145/C
来源:牛客网

牛市的幸存的先民在流星雨之后就忍痛离开了这片土地,选择迁徙,在迁徙的途中,他们需要渡过一条河。因为牛市的树木在流星雨中被严重破坏,所以他们只造出了一艘小船,船太小了,一次只能乘坐两人。
牛市的先民们每个人划船的速度都不尽相同,所以每个人都有一个渡河时间T,为了保证船的平衡,当穿上有两个人的时候,需要他们按照慢的那个人的速度划船,也就是说船到达对岸的时间等于船上渡河时间长的那个人的时间。
现在已知N个人的渡河时间T,请问最少要花费多少时间,才能使所有人都过河。
输入描述:
输入文件第一行为先民的人数N(N\leq 100000)(N≤100000),以下有N行,每行一个整数为每个人的渡河时间。
输出描述:
输出文件仅包含一个数,表示所有人都渡过河的最少渡河时间。
示例1
输入
复制
4
5
7
11
16
输出
复制
42
说明
首先1,2先到河对岸花费7,然后1回来花费5,3,4到河对岸花费16,2回来花费7,1,2再到河对岸花费7

假如不是第一个点和第二个点,最普遍的人,有哪些可能,它可以让最快的那个人先过来再过去,带着他,还可以他和上一个人两个人搭伙过日子,但是船还得过来,得先让最快的那个人把船开来,他们过,然后第二块的人去把最快的人接过去,所以初始化第一个人和第二个人,然后这样状态转移即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e5 + 10;
const int M = N * N;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int f[N];
int n;
int a[N];

signed main(){
	scanf("%lld", &n);
	for (int i = 1; i <= n; i ++){
		scanf("%lld", &a[i]);
	}
	
	sort(a + 1, a + 1 + n);
	
	memset(f, 0x3f, sizeof f);
	f[1] = a[1];
	f[2] = a[2];
	
	for (int i = 3; i <= n; i ++){
		f[i] = min(f[i - 1] + a[i] + a[1], f[i - 2] + a[i] + a[1] + 2 * a[2]);
	}
	
	cout << f[n] << endl;
	
	return 0;
}

牛牛的旅游纪念品
链接:https://ac.nowcoder.com/acm/contest/12145/E
来源:牛客网

题目描述
牛牛在牛市的旅游纪念商店里面挑花了眼,于是简单粗暴的牛牛决定——买最受欢迎的就好了。
但是牛牛的背包有限,他只能在商店的n个物品里面带m个回去,不然就装不下了。
并且牛牛希望买到的纪念品不要太相似,所以导购小姐姐帮助牛牛把纪念品全部排成了一行,牛牛只需要让选出来要买的m个物品中任意两个的位置差都大于等于k就行了。
现在告诉你这n个物品排成一行之后的受欢迎程度(可能是负数),求牛牛带回去的m个物品的最大欢迎度之和。

输入描述:
第一行三个数n,m,k

接下来一行,有n个整数,是n个物品按顺序的受欢迎程度。

输出描述:
输出一个数为题目所求的最大和
示例1
输入
复制
4 2 2
2 4 -6 1
输出
复制
5
说明
n\leq10000,m\leq100,m\leq nn≤10000,m≤100,m≤n,答案保证在int范围内,保证按照题目要求一定能取到m个物品
备注:
1

看数据范围,m的数据范围比较小,然后贪心无法做发现,然后就想到了动态规划,然后状态如何表示,这里拿二维表示第i个人选了j件物品的最大的和,当前这个人可以不选,就可以从上一个状态转移过来,当前这个人可以选,可以从-k个人的状态转移过来,因为有第一个·状态转移,所以这里也可以转移了更加靠前的状态

/*                         _
                        _ooOoo_
                       o8888888o
                       88" . "88
                       (| -_- |)
                  .'  \\|     |//  `.
                 /  \\|||  :  |||//  \
                /  _||||| -:- |||||_  \
                |   | \\\  -  /'| |   |
                | \_|  `\`---'//  |_/ |
                \  .-\__ `-. -'__/-.  /
              ___`. .'  /--.--\  `. .'___
           ."" '<  `.___\_<|>_/___.' _> \"".
          | | :  `- \`. ;`. _/; .'/ /  .' ; |
          \  \ `-.   \_\_`. _.'_/_/  -' _.' /
===========`-.`___`-.__\ \___  /__.-'_.'_.-'================
 
                  Please give me AC.
*/

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e4 + 10;
const int M = N * N;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int f[N][110];
int n, m, k;
int a[N];

signed main(){
	gt(n), gt(m), gt(k);
	
	memset(f, -0x3f, sizeof f);
	int maxd = -inf;
	for (int i = 1; i <= n; i ++){
		gt(a[i]);
		maxd = max(maxd, a[i]);
		f[i][1] = maxd;
	}
	
	for (int i = k + 1; i <= n; i ++){
		for (int j = 2; j <= m; j ++){
			f[i][j] = max(f[i - 1][j], f[i- k][j - 1] + a[i]);
		}
	}
	
	cout << f[n][m] << endl;

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值