软件学院蓝桥杯选拔赛
前言
我感觉这次选拔赛选拔只是一方面,更重要的一方面是让大家当做一次练习,即使比赛发挥的不好,也不要灰心,距离蓝桥杯省赛还有3个多月时间,期间如果好好学习,多多练习,一定会有很大的提高,也一定会在比赛中取得好成绩。这次编程题,除了第一题,其他题的出题思路是,让大多数人都能得分,但得不满分。主要目的是想让大家明白,这种比赛模式下,只要按照题意写了代码,就能得到部分的分数,但想要得满分,还需要算法和数据结构来进行优化。也是想让大家认识到算法与数据结构的作用。
1. 二进制
题目:
请写出int类型数20201209的二进制数。提示:答案的长度应为32位。
答案:
00000001001101000011111011111001
题解:
主要考察进制转换,将10进制转换为2进制,每次对2取模,并将这个数除以2,直到这个数变成0为止。不过需要注意的是32位,所以求的不够32位的,需要在前面补0。java的话可以直接调用相关的方法即可转换。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){string ans = "";int n = 20211209;for(int i = 1; i <= 32; i++){ans = (char) (n%2+'0') + ans;n = n/2;} cout << ans;
}
当然由于是填空题,也可以自行手算,按照10进制与2进制的关系推,也很容易得出正确答案。但还有一个最简单快速方法使用电脑自带的计算器,切换成程序员模式…
2. 日期计算
题目:
Hhy作为一名资深淀粉,他非常喜欢7这个数字,他想知道从EDG成立到夺得2021年S11全球总决赛冠军的这些天中(包括建队与夺冠这两天),年月日中一共出现了多少次7这个数字,比如2017年7月17日这一天共出现了3次7。
提示:
EDG电子竞技俱乐部(EDward Gaming),简称EDG,是一家中国电子竞技俱乐部,于2013年9月13日在广州成立。
2015年5月11日,在美国佛罗里达州塔拉哈西的MSI季中赛总决赛中,EDG以3:2战胜韩国战队SKT1,夺得英雄联盟季中赛世界冠军。
2021年11月7日,在冰岛雷克雅未克的英雄联盟全球总决赛中,EDG战队3:2战胜DK战队夺LPL第三座S赛冠军。
答案:
907
题解
题目求2013年9月13日到2021年11月7日中间出现过多少次7。可以通过编程来解决,下面的代码也是一个求日期问题的标准代码模板,不太会的同学可以参考一下。
但其实可以很容易发现,本题中7的出现次数与是不是闰年没有关系,并且出现次数的有一定的规律,比如年份中只有2017这一年包含着7,月份中只有7月包含着7,天中只要7,17,27这三天包含着7。因此即便不会用代码计算,也可以很容易的找规律的出。(如果不会编程还没找到规律,一个一个数也是一种办法…)
代码:
#include<bits/stdc++.h>
using namespace std;int month[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};int jud(int f){int ans = 0;while(f){if(f%10 == 7) ans++;f = f/10;}return ans;
}
int main(){int y1 = 2013, m1 = 9, d1 = 13;int y2 = 2021, m2 = 11, d2 = 7;int ans = jud(y1) + jud(m1) + jud(d1), f = 5;while(1){if(y1%4 == 0 && y1%100!=0 || y1%400 == 0) month[2] = 29;d1++;if(d1 > month[m1]){d1 = 1; m1++;if(m1 > 12){m1 = 1; y1++;}}ans += jud(y1) + jud(m1) + jud(d1);month[2] = 28;if(y1 == y2 && m1 == m2 && d1 == d2) break;}cout << ans;
}
3 杨辉三角
题目:
杨辉三角,是二项式系数在三角形中的一种几何排列。他有很多优美的性质。其中一条可以非常容易发现,他的每个数等于它上方两数之和。求出杨辉三角中第123456行,从左到右数第123453个数的值。
答案:
472514398
题解:
这道题有很多方法可以解决。
一、可以按照题目中给出的性质一行一行的推出最后的结果。需要注意的是如果开一个[123457][123457]的二维数组,会导致空间复杂度太大,程序运行不了。可以观察到每次计算时只需使用当前行和上一行的数组,因此可以使用滚动数组优化一下空间复杂度。最后的值很大,记得用long long 并且每次计算都对1e9+7取模。由于数据很大,程序需要跑很长时间(实测c++需要80s左右),不过是填空题影响不大。
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod = 1e9+7;
ll arr[2][130000];
int main(){int n, m;cin >> n >> m;arr[0][1] = 1; for(int i = 2; i <= n; i++){for(int j = 1; j <= i; j++){arr[1][j] = (arr[0][j-1] + arr[0][j] ) % mod;}for(int j = 1; j <= i; j++){arr[0][j] = arr[1][j];}}cout << arr[1][m];
}
二、可以利用杨辉三角的另一个性质解决,杨辉三角第n行m个数的值其实就是 Cn−1m−1C_{n-1}^{m-1}Cn−1m−1。因此求出C123455123452C_{123455}^{123452}C123455123452 的值即为答案,但直接求最后的数字很大,除法取模涉及到逆元相关的知识。可以将 C123455123452C_{123455}^{123452}C123455123452 等价变成 C1234553C_{123455}^{3}C1234553 通过编程或者计算器直接求出答案。
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod = 1e9+7;int main(){// 12345ll是定义为long long类型,防止计算溢出 cout << 123455ll*123454*123453 / 6 % mod;
}
三、还能通过其他方式求出答案,比如找规律、利用逆元等。杨辉三角有很多优美的性质,感兴趣的同学多了解一下。
4.sjm的棒棒糖
题目:
有一天sjm来到一个糖果店,糖果店的老板看sjm长的非常可爱,于是就跟sjm说,我给你两个数,一个是1136,一个1080, 你可以每次可以从中选一个大于 1 的数减 1,最后两个数都会减到 1,在减的过程中,如果两个数互质我就会给你一根棒棒糖。可是sjm对于质数很容易犯迷糊,请你帮帮sjm,找出她最多能得到多少根棒棒糖,并把最大数量输出出来。
答案:
2313
题解:
这题考察的知识点是动态规划,用使用记忆化递归也可以写,这里给出动态规划答案的代码与解析。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1500 + 7;
//f[i][j]代表着i 和 j 减到两个1互质的次数
int dp[maxn][maxn];
//求最大公因数
int gcd(int x,int y) {return y == 0 ? x : gcd(y,x % y);
}
//动态规划预处理,求出前1500的答案
void init() {for(int i = 1;i <= 1500;i++) {for(int j = 1;j <= 1500;j++) {//如果两个数 i 和 j 当前互质 那么他们减到1时互质的次数就等于// num1 = (i - 1) 和 j 减到减到1时互质的次数 + 1// num2 = i 和 (j - 1) 减到减到1时互质的次数 + 1//这两者中的较大值//i和j不互质则 num1 = (i - 1) 和 j 减到减到1时互质的次数 num2类似int num1 = dp[i - 1][j] + (gcd(i,j) == 1);int num2 = dp[i][j - 1] + (gcd(i,j) == 1);dp[i][j] = max(num1,num2);}}
}
int main() {init();int a,b;scanf("%d%d",&a,&b);printf("%d\n",dp[a][b]);return 0;
}
5. Beabled不相信完型填空有套路
题目
Beabled(以下简称小B)时长仰望星空的时候思考人生,静谧无人的今夜也不例外。小B回想起了高中的往事,英语是小B擅长的学科,其中一个原因是小B很喜欢高中的英语老师。有一次考前英语老师说完型填空有一个套路,每篇完型填空的答案都是A、B、C、D各五个,小B很信任老师,所以记下了这个套路。
小B在使用这个套路考试一年后,才想起要验证一下这个套路的正确性,于是他找到了至今为止考过的所有完型填空的答案,把这些答案拼成一个字符串,统计一下每个选项出现的个数,然后验证这个套路是否正确。如果每个选项出现的个数相等,则视为这个套路是正确的,输出“Beabled like English teacher the most!”;反之错误,则输出“Beabled don’t believe that cloze has a routine!”。
输入格式
输入一行字符串S代表出现过的所有选项。
输出格式
每个选项出现的数量相同,输出“Beabled最喜欢英语老师啦”。
不相同,输出“Beabled不相信完形填空有套路!”。
题解
本场签到题,使用字符串或者char数组读入后,遍历一遍统计A、B、C、D的个数,最后判断数量是否都一样即可。
代码
#include <iostream>
using namespace std;
string s;
int main()
{cin >> s;int a = 0, b = 0, c = 0, d = 0;for (int i = 0; i < s.size(); i++) {if (s[i] == 'A') a++;else if (s[i] == 'B') b++;else if (s[i] == 'C') c++;else d++;}if (a == b && b == c && c == d) printf("Beabled like English teacher the most!");else printf("Beabled don't believe that cloze has a routine!");return 0;
}
6.四六级考试要来了
题目
马上就要四六级考试了,小T决定在接下来 n 天内好好背背单词,于是小T列了一个详细的计划表,写下了第 i 天要背的单词个数 ai ,但是小T是个大忙人,总会有各种事情影响小T的计划,于是小T根据自己的日程安排更改计划,比如第 i 到 j 天,少背 x 个单词,第 k 到 l 天,多背 y 个单词。但是小T改计划太多次了,希望你能够根据修改记录,计算出他的单词计划。
输入格式
第一行有两个整数 n,k,代表天数与更改计划的次数。
第二行有 n 个数,第i个数则代表最初计划的第i天所背的单词数。
接下来 k 行,每行有三个数,i, j, x 代表给第i天到第j天内,单词计划改变x个。
1 <= i <= j <= n, -1e6 <= x <= 1e6。
输出格式
输出仅一行,代表修改计划后,每天所背的单词数,用空格隔开
输入样例
3 2
1 2 3
1 2 -1
1 3 2
输出样例
2 3 5
数据范围
数据范围:
对于百分之60的数据 0 <= n, k <= 1000
对于百分之100的数据 0 <= n, k <= 1e5
题解
本题为经典的差分算法模板题,
小T每一次修改都是对某个连续区间内的元素进行相同的更改,如果仅仅将目标修改的区间遍历一遍,逐个更改,时间复杂度将会达到O(n^2)的级别,显然是无法通过全部数据点的。所以要使用差分算法,对某个区间进行修改的操作优化为O(1)。
需要注意的是本题x的数值范围为1e6,n,k的范围是1e5,在极端的数据情况下最大数据范围为1e11,使用int会产生溢出,需要使用long long。
当然使用树状数组或者线段树实现区间修改、查询也能够通过本题,不过有点大材小用的感觉了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
long long q[N];
int n, m, r, l, c;
int main(){ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i=1;i<=n;i++){int t;cin >> t;//构建差分数组q[i+1] -= t; q[i] += t;}while(m--){cin >> l >> r >> c;//将l-r区间内都加上cq[r+1] -= c;q[l] += c;}for(int i=1;i<=n;i++){//前缀和运算q[i] += q[i-1];cout << q[i];if(i != n)cout << " ";}return 0;
}
7.干饭人
题目
餐厅每到中午下课,人就会超级多。同学们为了可以早点买到饭,在下课后都会马上冲到餐厅去买饭。
已知某班有 n 名同学,学号为1,2,……,n。一到下课,所有同学都同时往餐厅内的m个窗口跑去,餐厅窗口用1,2,……,m编号。第 i 号同学的跑步速度为 vi ,目标窗口数为 mi假设今天餐厅只为此班同学开放,如果有同学同时跑到一个窗口,则以学号小的学生站在前面。现在你根据已知信息,输出每个窗口所排的队列
输入格式
第一行为两个整数 n , m ,表示 n 名学生, m 个窗口
下面有 n 行,第 i 行包含3个整数 a , b ,表示第 i 个同学的跑步速度和目标要去的餐厅窗口。
输出格式
输出 n 行,第 i 行表示第 i 个窗口所排的队列,学号之间用空格隔开
(如果没有同学前往此窗口,则输出 0 )
输入样例
5 3
10 8
2 1
3 2
4 1
3 2
1 2
输出样例
3 1
2 4 5
0
题解
本题所考察的算法为排序算法和结构体、c++STL的应用
首先观察本题数据范围, 0 <= n <= 2e6, 若是选择O(n^2)(如冒泡排序)或者O(nlogn)(如快速排序)的排序算法,则一定会有部分数据点出现超时的情况。
所以需要选择时间复杂度为O(n)的算法,本次通过桶排序的思想,通过空间换时间来解决此题目,需要将速度和学号按照一定的规则排序,可以发现题目中输入数据是按照学号从小到大输入的,因此学号是有序的,只需对速度进行排序,窗口数量<=10,0<速度<=1000,所以只需使用一个vector arr[15][1005]数组按照窗口和速度来存储数据,里面的的数据无需排序即为有序的;
冒泡排序代码(只能过百分之40的数据):
#include<bits/stdc++.h>
#define fi first
#define se second
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
vector<pair<int, int>> arr[maxn];int main(){io;int n, m, a, b;cin >> n >> m;for(int i = 1; i <= n; i++){cin >> a >> b;arr[b].push_back({a,i});}for(int i = 1; i <= m; i++){if(arr[i].size() == 0){cout << 0 << '\n';}else{for(int j = 0; j < arr[i].size(); j++){// 冒泡排序for(int k = 0; k < arr[i].size()-j-1; k++)if(arr[i][k].fi < arr[i][k+1].fi ){pair<int, int> t = arr[i][k];arr[i][k] = arr[i][k+1];arr[i][k+1] = t;}}for(int j = 0; j < arr[i].size(); j++){if(j !=0 ) cout << ' '; cout << arr[i][j].se;}cout << '\n';}}
}
快速排序代码(只能过百分之80的数据)
#include<bits/stdc++.h>
#define fi first
#define se second
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long ll;
//const int maxn = 5e6+10;
vector<pair<int, int>> arr[15];// sort的排序函数
bool cmp(pair<int, int> a, pair<int, int> b){if(a.fi == b.fi){return a.se < b.se;}return a.fi > b.fi;
}int main(){io;int n, m, a, b;cin >> n >> m;for(int i = 1; i <= n; i++){cin >> a >> b;arr[b].push_back({a,i});}for(int i = 1; i <= m; i++){if(arr[i].size() == 0){cout << 0 << '\n';}else{sort(arr[i].begin(), arr[i].end(), cmp);for(int j = 0; j < arr[i].size(); j++){if(j != 0) cout << ' ';cout << arr[i][j].se;}cout << '\n';}}
}
桶排序代码(正解)
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long ll;
vector<int> arr[15][1005];int main(){io;int n, m, a, b;cin >> n >> m;for(int i = 1; i <= n; i++){cin >> a >> b;arr[b][a].push_back(i);}for(int i = 1; i <= m; i++){int s = 0;for(int j = 1000; j >= 1; j--){for(int k = 0; k < arr[i][j].size(); k++){if(s == 1) cout << ' ';cout << arr[i][j][k];s = 1;}}if(s == 0) cout << 0; cout << '\n';}
}
8.跟djh一起数数的日子
djh有一天发现了一道题,想了很久却一直做不对。题意为:给定两个整数 a 和 b,求 a 和 b之间的所有数字中 0∼9 的出现次数。
例如,a=1024,b=1032,则 a 和 b 之间共有 9 个数如下:
1024 1025 1026 1027 1028 1029 1030 1031 1032
其中 0
出现 10 次,1
出现 10 次,2
出现 7次,3
出现 3次等等…
输入格式
输入包含多组测试数据。
每组测试数据占一行,包含两个整数 a 和 b。
当读入一行为 0 时,表示输入终止,且该行不作处理。
输出格式
每组数据输出一个结果,每个结果占一行。
每个结果包含十个用空格隔开的数字,第一个数字表示 0
出现的次数,第二个数字表示 1
出现的次数,以此类推。
数据范围
对于百分之10的数据 -10^3 < a , b < 10^3
对于百分之50的数据 -10^6 < a , b < 10^6
对于百分之70的数据 -10^9 < a , b < 10^9
对于百分之100的数据 -10^14 < a , b < 10^14
题解
本次暴力枚举O(n)的时间复杂度可以过百分之50的数据,正解是通过寻找数字中单个数字出现次数的规律来得出正确答案。可以通过规律求得1-n中单个数字k出现的次数(具体看代码注释),此外数据有负数,需要分类讨论一下。
代码
暴力代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;ll arr[15];
void cnt(ll n){if(n == 0) arr[0]++;while(n){arr[n%10]++;n = n/10;}
}
int main(){ll a, b;cin >> a >> b;ll ans = 0;for(int i = a; i <= b; i++){cnt(abs(i));}for(int i = 0; i <= 9; i++){cout << arr[i];if(i != 9) cout << ' ';}
}
正解
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL a, b;//统计这个数字有几位
int dgt(LL x)
{int res = 1;while(x){x /= 10;res++;}return res;
}//统计1到n之间 i这个数字一共出现几次
LL cnt(LL n, int i)
{//d为n的位数 int d = dgt(n);//res为i一共出现几次 LL res = 0;//从右边第一位也就是个位开始找,看这位置上i一共出现了几次 for(int j = 1; j <= d; j++){//如果j是个位,p就是1,是十位,p就是10//l为j位左边部分的数值 也就是下面的abc//r为j位右边部分的数值//dj为改数字在j位上的值 //比如1234567//如果我看的是j = 4位也就是千位上,那么p= 1000,l=123,r=567,dj=4LL p = pow(10, j - 1), l = n / p / 10, r = n % p, dj = (n / p) % 10;//如果不是找0,那么计算第j位左边的整数小于l (xxx = 000 ~ abc - 1)的情况if(i) res += l * p;//如果是0,那么计算第j位左边的整数从1开始小于l (xxx = 001 ~ abc - 1)的情况if( !i && l) res += (l - 1) * p;//如果第j位比他大,那么加上 (xx = 000 ~ 999 )的情况if(dj > i && (i || l)) res += p;// 计算第j位左边的整数等于l (xxx = abc)的情况if(dj == i && (i || l)) res += (r + 1); }return res;
}int main()
{cin >> a > b;//判断是否都小于0,如果是则把他们转化为两个正数 if( a <= 0 && b <= 0){a = -a;b = -b;swap(a, b);} //找这个区间含几个i for(int i = 0; i <= 9; i++){//如果两个数都大于0 if( a >= 0 ){LL ans = cnt(b, i) - cnt(a - 1, i);if(i == 0 && a == 0) ans ++;cout << ans << " ";}//或者有一个小于0 else {LL ans = cnt(b, i) + cnt(-a, i);if(i == 0) ans ++;cout << ans <<" ";}}return 0;
}
另一种正解
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;// 求0-n中数字k出现的次数
ll cnt(ll n, int k){ll f = n;ll c = 1, res = 0, rem = 0;while(n){if(n % 10 == k) res += n / 10 * c + rem + 1;else res += (n / 10 + (n % 10 > k-1)) * c; rem += n % 10 * c;if(k == 0) res -= c;c *= 10;n /= 10;}if(k == 0) res += 1;return res;
}int main(){ll a, b;cin >> a >> b;for(int i = 0; i <= 9; i++){ll ans = 0;if(a >= 0 && b >= 0 || a <= 0 && b <= 0){if(a >= 0){ans += cnt( abs(b), i );if(a != 0) ans -= cnt( abs(a-1), i );}else{ans += cnt(abs(a), i );if(b != 0) ans -= cnt(abs(b+1), i );}}else{ans += cnt(0-a, i);ans += cnt(b, i);if(i == 0) ans--;}cout << ans;if(i!=9) cout << ' ';}
}
9. Beabled的奇妙梦中旅程
Beabled(简称小B)喜欢睡觉,有时候一睡就是一整天,因此小B因睡觉耽误了好多事而受责怪。有一次,小B下午有一个重要的会议,但是小B现在很困,所以小B决定午睡一会,但是很不巧,做梦的时候小B发现自己被困在梦里出不去了,小B非常慌忙,但是却无能为力。这时有一个士兵过来告诉小B,说他有办法让小B逃脱梦境,士兵说:
附近有n个城堡,小B所处的位置就是1号古堡,每个古堡之间原本有互通的道路,但是因为政权问题,部分道路被阻断,现只存在部分连通的道路,而且每条连通的道路长度也不尽相同。在第n个城堡里,有女巫可以制造摆脱梦境的解药,你去要找到这个女巫,才有机会摆脱梦境。
小B听完后很感动,正准备动身前往时,士兵把小B拦下,说:
别着急,我还没告诉你古堡的分布呢,你现在去怎么能找到?我将会告诉你有m条连通两个古堡的道路,以及每条道路的路径长度l,剩下的就交给你了。
小B深知是场梦,听完还是很感动!在筹备好行动路线后就以最短的路径前往第n个古堡了。
最终小B成功逃脱了梦境,赶上了重要的会议。
那么聪明的你一定也能算出小B前往第n个古堡最短的路径是多少吧?
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数a,b,c,表示存在一条从点a到b的有向边,边长为c。
输出格式
输出一个整数,表示小B从1号古堡到n号古堡的最短距离。
若路径不存在,则输出“小B is Game Over!”。
数据范围
对于百分之20的数据n,m<=50,
对于百分之40的数据,n<=500,m<=1000,
对于百分之70的数据,n<=5000,m<=10000,
对于百分之100的数据,n,m<=150000。
题解
本题主要考察最短路径算法,是一个标准的模板题,但也可以通过奇形怪状的暴力骗取部分的分数。
使用floyd算法可以通过百分之40的数据,使用朴素dijkstra可以通过百分之70的样例。正解为堆优化版的dijkstra。感兴趣的可以在网上搜索一下相关知识,有很多相关的博客。
这里给出堆优化版的dijkstra解析。
堆优化版的dijkstra是对朴素版dijkstra进行了优化,在朴素版dijkstra中时间复杂度最高的寻找距离最短的点O(n^2)可以使用最小堆优化。
- 一号点的距离初始化为零,其他点初始化成无穷大。
- 将一号点放入堆中。
- 不断循环,直到堆空。每一次循环中执行的操作为:
弹出堆顶(与朴素版diijkstra找到S外距离最短的点相同,并标记该点的最短路径已经确定)。
用该点更新临界点的距离,若更新成功就加入到堆中。
时间复杂度分析
寻找路径最短的点:O(n)
加入集合S:O(n)
更新距离:O(mlogn)
代码
#include<iostream>
#include<cstring>
#include<queue>using namespace std;typedef pair<int, int> PII;const int N = 150010; // 稀疏图用邻接表来存
int h[N], e[N], ne[N], idx;
int w[N]; // 用来存权重
int dist[N];
bool st[N]; // 如果为true说明这个点的最短路径已经确定
int n, m;
void add(int x, int y, int c)
{w[idx] = c; // 有重边也不要紧,假设1->2有权重为2和3的边,再遍历到点1的时候2号点的距离会更新两次放入堆中e[idx] = y; // 这样堆中会有很多冗余的点,但是在弹出的时候还是会弹出最小值2+x(x为之前确定的最短路径),并ne[idx] = h[x]; // 标记st为true,所以下一次弹出3+x会continue不会向下执行。h[x] = idx++;
}int dijkstra()
{memset(dist, 0x3f, sizeof(dist));dist[1] = 0;priority_queue<PII, vector<PII>, greater<PII>> heap; // 定义一个小根堆// 这里heap中为什么要存pair呢,首先小根堆是根据距离来排的,所以有一个变量要是距离,其次在从堆中拿出来的时 // 候要知道知道这个点是哪个点,不然怎么更新邻接点呢?所以第二个变量要存点。heap.push({ 0, 1 }); // 这个顺序不能倒,pair排序时是先根据first,再根据second,这里显然要根据距离排序while(heap.size()){PII k = heap.top(); // 取不在集合S中距离最短的点heap.pop();int ver = k.second, distance = k.first;if(st[ver]) continue;st[ver] = true;for(int i = h[ver]; i != -1; i = ne[i]){int j = e[i]; // i只是个下标,e中在存的是i这个下标对应的点。if(dist[j] > distance + w[i]){dist[j] = distance + w[i];heap.push({ dist[j], j });}}}if(dist[n] == 0x3f3f3f3f) return -1;else return dist[n];
}int main()
{memset(h, -1, sizeof(h));scanf("%d%d", &n, &m);while (m--){int x, y, c;scanf("%d%d%d", &x, &y, &c);add(x, y, c);}cout << dijkstra() << endl;return 0;
}
java版
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;public class Main{static int N = 150010;static int n;static int[] h = new int[N];static int[] e = new int[N];static int[] ne = new int[N];static int[] w = new int[N];static int idx = 0;static int[] dist = new int[N];// 存储1号点到每个点的最短距离static boolean[] st = new boolean[N];static int INF = 0x3f3f3f3f;//设置无穷大public static void add(int a,int b,int c){e[idx] = b;w[idx] = c;ne[idx] = h[a];h[a] = idx ++;}// 求1号点到n号点的最短路,如果不存在则返回-1public static int dijkstra(){//维护当前未在st中标记过且离源点最近的点PriorityQueue<PIIs> queue = new PriorityQueue<PIIs>();Arrays.fill(dist, INF);dist[1] = 0;queue.add(new PIIs(0,1));while(!queue.isEmpty()){//1、找到当前未在s中出现过且离源点最近的点PIIs p = queue.poll();int t = p.getSecond();int distance = p.getFirst();if(st[t]) continue;//2、将该点进行标记st[t] = true;//3、用t更新其他点的距离for(int i = h[t];i != -1;i = ne[i]){int j = e[i];if(dist[j] > distance + w[i]){dist[j] = distance + w[i];queue.add(new PIIs(dist[j],j));}}}if(dist[n] == INF) return -1;return dist[n];}public static void main(String[] args) throws IOException{BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String[] str1 = reader.readLine().split(" ");n = Integer.parseInt(str1[0]);int m = Integer.parseInt(str1[1]);Arrays.fill(h, -1);while(m -- > 0){String[] str2 = reader.readLine().split(" ");int a = Integer.parseInt(str2[0]);int b = Integer.parseInt(str2[1]);int c = Integer.parseInt(str2[2]);add(a,b,c);}System.out.println(dijkstra());}
}class PIIs implements Comparable<PIIs>{private int first;//距离值private int second;//点编号public int getFirst(){return this.first;}public int getSecond(){return this.second;}public PIIs(int first,int second){this.first = first;this.second = second;}@Overridepublic int compareTo(PIIs o) {// TODO 自动生成的方法存根return Integer.compare(first, o.first);}
}
10.夺宝游戏
题目描述
小H在玩一个游戏,游戏的规则是这样的:有一块3*n个格子组成的区域,即有3行n列,每一块格子上都有一个价值为v的宝物,游戏开始前小H在这块区域的起点:左上角(1,1)处,他需要要走到这块区域的终点:右下角(3,n)处。
他每次只能向右或者向下移动一格,不能向左或者向上移动。每走过一块格子就会将这块格子的宝物收入囊中(小H出生在(1,1)处,所以(1,1)处的宝物可以直接获得),到达终点后,小H拥有的宝物价值必须大于等于K,才能赢得游戏。请问小H有多少种走法能最终赢得游戏。
输入描述:
第一行两个正整数n, k。0 <= k <= 1e10。
接下来三行,每行n个整数,第i行第j个整数vij表示该块格子宝物的价值。0<=v<=1e4。
输出描述:
小H赢得游戏的不同方法数。
输入样例:
3 10
1 1 1
2 2 2
3 3 3
输出样例:
4
数据范围:
对于百分之10的数据n<=20,
对于百分之40的数据n<=5000,
对于百分之70的数据n<=50000,
对于百分之100的数据n<=500000。
题解
本题是这场比赛中相对比较难的一道题,但部分数据很水,依旧可以使用暴力骗取一些分数。
由于图只有3行,且不能往上走,因此小H只有两次向下走的机会。通过预处理前缀和做优化,然后枚举两次往下走的下标, 通过sum1[i]+sum2[j]−sum2[i−1]+sum3[n]−sum3[j−1]≥k 计算符合条件的数目,时间复杂度为O(n^2),可以通过百分之40的数据。
正解需要用到前缀和、树状数组(线段树)、离散化来解决。
首先预处理出每行的前缀和数组,接着注意到行数只有三,于是我们用 i 表示从第一行下到第二行的下标,用 j表示从第二行下到第三行的下标,那么问题可以转化成,求满足 sum1[i]+sum2[j]−sum2[i−1]+sum3[n]−sum3[j−1]≥k的 (i,j)对数。
我们对上式移项,变为 sum2[j]−sum3[j−1] ≥ k+sum2[i−1]−sum1[i]−sum3[n] (i<=j)。显然可以对其离散化后利用树状数组或者线段树进行求解。
时间复杂度: O(n×log(n))。
前缀和代码(通过百分之40数据)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 5e5+10;
ll arr[4][maxn];
ll sum[4][maxn];
int main(){ll n, k;cin >> n >> k;for(int i = 1; i <= 3; i++){for(int j = 1; j <= n; j++){cin >> arr[i][j];sum[i][j] = sum[i][j-1] + arr[i][j];}}ll ans = 0;for(int i = 1; i <= n; i++){for(int j = i; j <= n; j++){if(sum[1][i] + sum[2][j]- sum[2][i-1] + sum[3][n] - sum[3][j-1] >= k) ans++;}}cout << ans;
}
正解代码
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;typedef long long ll;
const int maxn = 1e6+10;
const int mod = 1e9+7;ll bit[maxn*4], n, k;void add(int p, ll x){ while(p <= n) bit[p] += x, p += p & -p;
}ll ask(ll p){ ll res = 0;while(p) res += bit[p], p -= p & -p;return res;
}
ll range_ask(ll l, ll r){ return ask(r) - ask(l - 1);
}vector<ll> alls; int find(ll x) {int l = 0, r = alls.size();while (l < r){int mid = l + r >> 1;if (alls[mid] >= x) r = mid;else l = mid + 1;}return r + 1;
}ll arr[4][maxn];
ll sum[4][maxn];
ll s2[maxn];
int main(){io;ll ans = 0;cin >> n >> k;for(int i = 1; i<=3; i++){for(int j = 1; j <= n; j++){cin >> arr[i][j];sum[i][j] = sum[i][j-1] + arr[i][j];}}for(int i = 1; i <= n; i++){s2[i] = sum[2][i]-sum[3][i-1];alls.push_back(s2[i]);}sort(alls.begin(), alls.end()); alls.erase(unique(alls.begin(), alls.end()), alls.end());for(int i = 1; i <= n; i++){add(find(s2[i]), 1);}for(int i = 1; i <= n; i++){ll sum1 = sum[1][i] - sum[2][i-1] + sum[3][n];ans = (ans + range_ask(find(k-sum1),n) );add(find(s2[i]), -1);}cout << ans;
}
软件学院蓝桥杯选拔赛相关推荐
- 【蓝桥杯选拔赛真题17】Scratch金字塔 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 Scratch金字塔 一.题目要求 1.准备工作 2.编程实现 二.题目分析 1.角色分析 2.背景分析 3.准备工作 三.解题思路 1.流程分析 2.流程图绘制 四.程序编写 五.考点分析 六 ...
- 【蓝桥杯选拔赛真题42】Scratch模拟画板 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 scratch模拟画板 一.题目要求 编程实现 二.案例分析 1.角色分析 2.背景分析 3.前期准备 三.解题思路 1.思路分析 2.详细过程 四.程序编写 五.考点分析 六.推荐资料 1.入 ...
- CCW2021蓝桥杯选拔赛(一)
总览 C - 云顶之弈! D - 签到啦! E - 栈! F - 数列的高度 G - 人潮汹涌 I - 乌鸦坐飞机! J - ICPC! K - 房子塌了! L - 小王蹬三轮 C - 云顶之弈! 最 ...
- 【蓝桥杯选拔赛真题01】Scratch消失的小猫 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 scratch消失的小猫 一.题目要求 1.准备工作 2.编程实现
- 【蓝桥杯选拔赛真题15】Scratch碰苹果游戏 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 scratch碰苹果 一.题目要求 编程实现 二.题目分析
- 【蓝桥杯选拔赛真题34】Scratch数苹果 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 scratch数苹果 一.题目要求 1.编程实现 2.评判标准
- 【蓝桥杯省赛真题2】Scratch射击螃蟹 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 scratch射击螃蟹 一.题目要求 1.准备工作 2.具体要求 二.题目分析
- 【蓝桥杯选拔赛真题09】Scratch小猫旅行 少儿编程scratch蓝桥杯选拔赛真题讲解
目录 scratch小猫旅行 一.题目要求 1.编程实现 2.具体要求
- 【蓝桥杯选拔赛真题50】Scratch小猫跑步 少儿编程scratch图形化编程 蓝桥杯选拔赛真题讲解
目录 scratch小猫跑步 一.题目要求 编程实现 二.案例分析 1.角色分析
最新文章
- 2022-2028年中国机制砂石行业投资分析及前景预测报告
- PMON failed to acquire latch, see PMON dump
- Linux上vi的使用教程
- SSVEP脑机接口及数据集处理
- zookeeper系列(九)zookeeper的会话详解
- input date保存值_涛哥文集(36):R keras保存和还原模型
- 毫米波雷达_最新的7个毫米波雷达应用案例
- linux 树状结构图,linux下tree指令的用法, 树状图列出目录, 树状图逐级列出目录...
- Vue框架搭建快速入门
- mysql 按月建表_MySQL之存储过程按月创建表
- C++ DLL导出接口
- H265框架编码流程(一)
- 以太坊系列之十四: solidity特殊函数
- vue router hash和history的区别_react-router-v4
- pr.exe、Churrasco.exe、ms10048.exe用法及提权原理 上帝模式
- 一图掌握ICT项目管理流程图【实例】
- 如何用html代码做表格里的对角线,如何用用div+css模拟表格对角线
- **汉服有哪些基本形制呢**
- CentOS 7 下安装五笔输入法
- 备战Java后端【Day6】
热门文章
- 实验吧-因缺思厅的绕过writeup
- 【黑马头条训练营】day02-黑马头条-App端文章展示
- 梅克尔工作室-赵一帆-鸿蒙笔记4
- 【多输入模型 Multiple-Dimension 数学原理分析以及源码详解 深度学习 Pytorch笔记 B站刘二大人 (6/10)】
- 对于Java8的新特性,应该了解(掌握)的哪些
- win10自动添加打印机设备
- World Locking Tools for Unity (三)原理部分
- Arduino+DHT11温湿度传感器+12864oled显示温湿度
- 学了php能做什么工作吗,学会ps能做什么工作
- 前端开发如何获取视频第一帧作为封面