2020蓝桥杯省赛第二场c++B组
为了蓝桥杯补一下之前的真题(临时抱佛脚)。
1、特殊年份
今年是 2021 年,2021 这个数字非常特殊,它的千位和十位相等,个位比百位大 1,我们称满足这样条件的年份为特殊年份。
输入 5 个年份,请计算这里面有多少个特殊年份。
输入格式
输入 5 行,每行一个 4 位十进制数(数值范围为 1000 至 9999),表示一个年份。
输出格式
输出一个整数,表示输入的 5个年份中有多少个特殊年份。
输入样例:
2019
2021
1920
2120
9899
输出样例:
2
样例解释
2021 和 9899 是特殊年份,其它不是特殊年份。
解题思路:一个简单题,跟着题目打就行了。
#include<iostream>
using namespace std;
int main(){
int cnt = 0;
for(int i = 1 ; i <= 5 ; i++){
string s;
cin >> s ;
if(s[0] == s[2] && s[3] - s[1] == 1)
cnt++;
}
cout << cnt <<endl;
return 0;
}
2、完全平方数
一个整数 a 是一个完全平方数,是指它是某一个整数的平方,即存在一个整数 b,使得 a = b 2 a=b^2 a=b2。
给定一个正整数 n,请找到最小的正整数 x,使得它们的乘积是一个完全平方数。
输入格式
输入一行包含一个正整数n。
输出格式
输出找到的最小的正整数 x。
数据范围
对于 30% 的评测用例,1≤n≤1000,答案不超过 1000。
对于 60% 的评测用例,
1
≤
n
≤
1
0
8
1≤n≤10^8
1≤n≤108,答案不超过
1
0
8
10^8
108。
对于所有评测用例,1≤n≤1012,答案不超过 1012。
输入样例1:
12
输出样例1:
3
输入样例2:
15
输出样例2
15
解题思路:我们发现如果a = b2,那么a的质因数是偶数倍。所以我们可以分解a的质因数个数将质因数为奇数的相乘。
#include<iostream>
using namespace std;
typedef long long ll;
int main(){
ll n ;
cin >> n ;
ll i = 2 ;
ll sum = 1;
while(i*i <= n){//这里一定要写成i*i <= n , 之前因为脑子抽了,写成i<=n,wrong了。
ll cnt = 0;
while(n%i == 0){
n/=i;
cnt++;
}
if(cnt%2==1)
sum*=i;
i++;
}
if(n != 1)
sum*=n;
cout <<sum <<endl;
return 0;
}
3、负载均衡
有 n 台计算机,第 i 台计算机的运算能力为 vi。
有一系列的任务被指派到各个计算机上,第 i 个任务在 ai 时刻分配,指定计算机编号为 bi,耗时为 ci 且算力消耗为 di。
如果此任务成功分配,将立刻开始运行,期间持续占用 bi 号计算机 di 的算力,持续 ci 秒。
对于每次任务分配,如果计算机剩余的运算能力不足则输出 −1,并取消这次分配,否则输出分配完这个任务后这台计算机的剩余运算能力。
输入格式
输入的第一行包含两个整数 n,m,分别表示计算机数目和要分配的任务数。
第二行包含 n 个整数 v1,v2,⋅⋅⋅vn,分别表示每个计算机的运算能力。
接下来 m 行每行 4 个整数 ai,bi,ci,di,意义如上所述。数据保证 ai 严格递增,即 ai<ai+1。
输出格式
输出 m 行,每行包含一个数,对应每次任务分配的结果。
数据范围
对于 20% 的评测用例,n,m≤200。
对于 40% 的评测用例,n,m≤2000。
对于所有评测用例,1≤n,m≤200000,1≤ai,ci,di,vi≤109,1≤bi≤n。
输入样例:
2 6
5 5
1 1 5 3
2 2 2 6
3 1 2 3
4 1 6 1
5 1 3 3
6 1 3 4
输出样例:
2
-1
-1
1
-1
0
样例解释
时刻 1,第 1 个任务被分配到第 1 台计算机,耗时为 5,这个任务时刻 6 会结束,占用计算机 1 的算力 3。
时刻 2,第 2 个任务需要的算力不足,所以分配失败了。
时刻 3,第 1 个计算机仍然正在计算第 1 个任务,剩余算力不足 3,所以失败。
时刻 4,第 1 个计算机仍然正在计算第 1 个任务,但剩余算力足够,分配后剩余算力 1。
时刻 5,第 1 个计算机仍然正在计算第 1,4 个任务,剩余算力不足 4,失败。
时刻 6,第 1 个计算机仍然正在计算第 4 个任务,剩余算力足够,且恰好用完。
**解题思路:**因为任务是实时的,所以我们需要在当前时间时候去将选择的这台机器的计算能力的实时能力算出来(即将机器之前时间就完成的任务占用的能力释放出来)。可以用map去存每一台机器的某个任务结束时间和任务消耗的能力。再去判定这台机器的剩余能力是否大于当前任务所需的能力。
#include <iostream>
#include <cstring>
#include <algorithm>
#include<map>
using namespace std;
const int N = 2e5+10;
int v[N];
int t[N];
map <int ,int > mp[N];///n台机器的中每个任务的结束时间和任务能力
int main()
{
int n ,m;
cin >> n >> m ;
for(int i = 1 ; i<= n ; i++){
cin >> v[i];
}
for(int i = 1 ; i <= m ; i++){
int a , b ,c , d;
cin >> a >> b >> c >> d;
int sum = 0 ;
for(auto it : mp[b]){//这台机器是否之前的任务是否完成并释放占有的能力.
if(it.first <= a){
sum+=it.second;
mp[b].erase(it.first);
}
}
v[b] +=sum;
if(v[b] >= d){
v[b]-=d;
mp[b][a+c] += d;//这里必须是+= , 不能是 = ,不然就可能覆盖了在[a+c]时刻完成任务的能力,这时刻可能有多个任务完成。
cout << v[b]<<endl;
}else{
cout << "-1"<<endl;
}
}
return 0;
}
4、国际象棋
众所周知,“八皇后” 问题是求解在国际象棋棋盘上摆放 8 个皇后,使得两两之间互不攻击的方案数。
已经学习了很多算法的小蓝觉得 “八皇后” 问题太简单了,意犹未尽。作为一个国际象棋迷,他想研究在 N×M 的棋盘上,摆放 K 个马,使得两两之间互不攻击有多少种摆放方案。
由于方案数可能很大,只需计算答案除以 1000000007 (即 109+7) 的余数。
如下图所示,国际象棋中的马摆放在棋盘的方格内,走 “日” 字,位于 (x,y) 格的马(第 x 行第 y 列)可以攻击 (x+1,y+2)、(x+1,y−2)、(x−1,y+2)、(x−1,y−2)、(x+2,y+1)、(x+2,y−1)、(x−2,y+1) 和 (x−2,y−1) 共 8 个格子。
输入格式
输入一行包含三个正整数 N,M,K,分别表示棋盘的行数、列数和马的个数。
输出格式
输出一个整数,表示摆放的方案数除以 1000000007 (即 109+7) 的余数。
数据范围
对于 5% 的评测用例,K=1;
对于另外 10% 的评测用例,K=2;
对于另外 10% 的评测用例,N=1;
对于另外 20% 的评测用例,N,M≤6,K≤5;
对于另外 25% 的评测用例,N≤3,M≤20,K≤12;
对于所有评测用例,1≤N≤6,1≤M≤100,1≤K≤20。
输入样例1:
1 2 1
输出样例1:
2
输入样例2:
4 4 3
输出样例2:
276
输入样例3:
3 20 12
输出样例3:
914051446
**解题思路:**一开始也没什么思路,但看到这种题应该就是状压DP,但自己不怎么会写,于是看了y总的视频。他的思路就是我们放我们从上往下递推的放棋子,我们会发现棋子能否放去确于上面两行的对应位置上是否有相对应的棋子可以想回攻击。
这样我们的状态就要有前面的两行。所以 d p [ i ] [ a ] [ b ] [ k ] dp[i][a][b][k] dp[i][a][b][k](表示前i行中a,b两行中有k个棋子已经放置了的方案数)
#include<iostream>
using namespace std;
const int N = (1 << 6) +10;
const int M = 110;
const int K = 25;
const int MOD = 1000000007;
int dp[M][N][N][K];//这里因为n<=6,m<=100所以我们把棋盘旋转90度,这样我们的状态就可以减少很多。
int get_count(int x){//计算x有多少个1.
int res = 0;
while(x){
x -= x &(-x);
res++;
}
return res;
}
int main(){
int n , m ,k;
cin >> n >> m >> k;
dp[0][0][0][0] = 1;//初始化0,0,0,0,不然没有值
for(int i = 1 ; i <= m ;i++){
for(int a= 0 ; a < 1<<n ; a++){//前面第二行的状态
for(int b = 0 ; b < 1 << n ;b++){//前面第一行的状态
if(a&(b << 2)|| b&(a << 2)) continue;//判断前两行的状态是否满足棋子不想互攻击
for(int c = 0 ; c <1 << n; c++){//当前放棋子这一行
if(c&(a<<1)||a&(c<<1)) continue;//这行的前面第二行去比较
if(c&(b<<2)||b&(c<<2)) continue;//这行的和前面一行去比较
int t = get_count(c);//计算这行的状态,即有多少个1
for(int j = t ; j <= k ; j++){
dp[i][b][c][j] = dp[i][b][c][j]+dp[i-1][a][b][j-t];//状态的转移.
dp[i][b][c][j] %=MOD;
}
}
}
}
}
int ans = 0;//最后将所有的情况相加
for(int a = 0 ;a < 1 <<n ;a++){
for(int b = 0 ; b < 1 << n ; b++){
ans = ans + dp[m][a][b][k];
ans %=MOD;
}
}
cout << ans <<endl;
return 0;
}