前言

我感觉这次选拔赛选拔只是一方面,更重要的一方面是让大家当做一次练习,即使比赛发挥的不好,也不要灰心,距离蓝桥杯省赛还有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)可以使用最小堆优化。

  1. 一号点的距离初始化为零,其他点初始化成无穷大。
  2. 将一号点放入堆中。
  3. 不断循环,直到堆空。每一次循环中执行的操作为:
    弹出堆顶(与朴素版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;
}

软件学院蓝桥杯选拔赛相关推荐

  1. 【蓝桥杯选拔赛真题17】Scratch金字塔 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 Scratch金字塔 一.题目要求 1.准备工作 2.编程实现 二.题目分析 1.角色分析 2.背景分析 3.准备工作 三.解题思路 1.流程分析 2.流程图绘制 四.程序编写 五.考点分析 六 ...

  2. 【蓝桥杯选拔赛真题42】Scratch模拟画板 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 scratch模拟画板 一.题目要求 编程实现 二.案例分析 1.角色分析 2.背景分析 3.前期准备 三.解题思路 1.思路分析 2.详细过程 四.程序编写 五.考点分析 六.推荐资料 1.入 ...

  3. CCW2021蓝桥杯选拔赛(一)

    总览 C - 云顶之弈! D - 签到啦! E - 栈! F - 数列的高度 G - 人潮汹涌 I - 乌鸦坐飞机! J - ICPC! K - 房子塌了! L - 小王蹬三轮 C - 云顶之弈! 最 ...

  4. 【蓝桥杯选拔赛真题01】Scratch消失的小猫 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 scratch消失的小猫 一.题目要求 1.准备工作 2.编程实现

  5. 【蓝桥杯选拔赛真题15】Scratch碰苹果游戏 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 scratch碰苹果 一.题目要求 编程实现 二.题目分析

  6. 【蓝桥杯选拔赛真题34】Scratch数苹果 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 scratch数苹果 一.题目要求 1.编程实现 2.评判标准

  7. 【蓝桥杯省赛真题2】Scratch射击螃蟹 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 scratch射击螃蟹 一.题目要求 1.准备工作 2.具体要求 二.题目分析

  8. 【蓝桥杯选拔赛真题09】Scratch小猫旅行 少儿编程scratch蓝桥杯选拔赛真题讲解

    目录 scratch小猫旅行 一.题目要求 1.编程实现 2.具体要求

  9. 【蓝桥杯选拔赛真题50】Scratch小猫跑步 少儿编程scratch图形化编程 蓝桥杯选拔赛真题讲解

    目录 scratch小猫跑步 一.题目要求 编程实现 二.案例分析 1.角色分析

最新文章

  1. 2022-2028年中国机制砂石行业投资分析及前景预测报告
  2. PMON failed to acquire latch, see PMON dump
  3. Linux上vi的使用教程
  4. SSVEP脑机接口及数据集处理
  5. zookeeper系列(九)zookeeper的会话详解
  6. input date保存值_涛哥文集(36):R keras保存和还原模型
  7. 毫米波雷达_最新的7个毫米波雷达应用案例
  8. linux 树状结构图,linux下tree指令的用法, 树状图列出目录, 树状图逐级列出目录...
  9. Vue框架搭建快速入门
  10. mysql 按月建表_MySQL之存储过程按月创建表
  11. C++ DLL导出接口
  12. H265框架编码流程(一)
  13. 以太坊系列之十四: solidity特殊函数
  14. vue router hash和history的区别_react-router-v4
  15. pr.exe、Churrasco.exe、ms10048.exe用法及提权原理 上帝模式
  16. 一图掌握ICT项目管理流程图【实例】
  17. 如何用html代码做表格里的对角线,如何用用div+css模拟表格对角线
  18. **汉服有哪些基本形制呢**
  19. CentOS 7 下安装五笔输入法
  20. 备战Java后端【Day6】

热门文章

  1. 实验吧-因缺思厅的绕过writeup
  2. 【黑马头条训练营】day02-黑马头条-App端文章展示
  3. 梅克尔工作室-赵一帆-鸿蒙笔记4
  4. 【多输入模型 Multiple-Dimension 数学原理分析以及源码详解 深度学习 Pytorch笔记 B站刘二大人 (6/10)】
  5. 对于Java8的新特性,应该了解(掌握)的哪些
  6. win10自动添加打印机设备
  7. World Locking Tools for Unity (三)原理部分
  8. Arduino+DHT11温湿度传感器+12864oled显示温湿度
  9. 学了php能做什么工作吗,学会ps能做什么工作
  10. 前端开发如何获取视频第一帧作为封面