2020年蓝桥杯暑假第3次练习赛(C++组)

文章目录

    • A 试题 算法训练 结点选择
      • 难度:高 题目类型:动态规划-树型动态规划
    • B 试题 算法训练 动态数组使用
      • 难度:低 题目类型:数学-求和 求平均值
    • C 试题 算法提高 身份证号码升级
      • 难度:低 题目类型:基本算法-模拟
    • D 试题 算法提高 排列数
      • 难度:中 题目类型:数学-排列组合 / DFS
    • E 试题 算法提高 P1001
      • 难度:中 题目类型:基本算法-模拟 / 高精度乘法
    • F 试题 历届试题 买不到的数目
      • 难度:高 题目类型:数学-扩展欧几里得 / 动态规划

A 试题 算法训练 结点选择

难度:高 题目类型:动态规划-树型动态规划

提交此题 评测记录

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?

输入格式

第一行包含一个整数 n 。

接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。

接下来一共 n-1 行,每行描述树上的一条边。

输出格式

输出一个整数,代表选出的点的权值和的最大值。

样例输入

5

1 2 3 4 5

1 2

1 3

2 4

2 5

样例输出

12

样例说明

选择3、4、5号点,权值和为 3+4+5 = 12 。

数据规模与约定

对于20%的数据, n <= 20。

对于50%的数据, n <= 1000。

对于100%的数据, n <= 100000。

权值均为不超过1000的正整数。

解题思路:
树形动态规划

核心,即状态转移方程:

2020年蓝桥杯暑假第3次练习赛(C++组)_第1张图片
代码如下:

#include 
using namespace std;

int dp[100010][2];
vector >link;
 //深度遍历,先深入到叶子结点,然后一层一层往上回升,一直到根结点,即第一个结点(初始pre为0是因为根结点没有父结点,这里用0表示)
void dfs(int x, int pre)
{
	int temp;
	for(int i = 0; i < link[x].size(); i++)
	{
		temp = link[x][i];//temp代表与x相连的子节点,x可以理解为父结点
		if(temp != pre)//如果指向的子结点和父结点重合,则说明这个结点是叶子结点,不需要进一步dp
		{
			dfs(temp,x);
        	//深度遍历到最里面的叶子结点的父结点 
			dp[x][0] += max(dp[temp][0],dp[temp][1]);//   父结点(不选) += max(子结点(不选),子结点(选))
			dp[x][1] += dp[temp][0]; //   父结点(选) += 子结点(不选)
		 } 
	}
} 
int main(int argc, char** argv) {
	//输入结点个数 
	int n;
	cin>>n;
	//输入各个结点的权值 
	for(int i = 1; i <= n; i++)
	{
		cin>>dp[i][1];
	}
	//输入n-1条边 
	link.resize(n+1);
	int a,b; 
	for(int i = 1; i <= n-1; i++)
	{
		cin>>a>>b;
		link[a].push_back(b);
		link[b].push_back(a);
	}
	//深度优先遍历,从第一个结点开始遍历
	dfs(1,0);
	//输出权值和的最大值 
	cout<

B 试题 算法训练 动态数组使用

难度:低 题目类型:数学-求和 求平均值

提交此题 评测记录

资源限制

时间限制:1.0s 内存限制:512.0MB

从键盘读入n个整数,使用动态数组存储所读入的整数,并计算它们的和与平均值分别输出。要求尽可能使用函数实现程序代码。平均值为小数的只保留其整数部分。
样例输入:   
5   
3 4 0 0 2  
样例输出:  
9  1
样例输入:   
7  
3 2 7 5 2 9 1  
样例输出:  
29  4
#include
using namespace std;
int main(){
	int *a,n;
	cin>>n;
	a=new int[n];
	int sum=0;
	for(int i=0; i>a[i];
		sum+=a[i];
	}
	cout<

C 试题 算法提高 身份证号码升级

难度:低 题目类型:基本算法-模拟

提交此题 评测记录

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

从1999年10月1日开始,公民身份证号码由15位数字增至18位。(18位身份证号码简介)。升级方法为:

1、把15位身份证号码中的年份由2位(7,8位)改为四位。

2、最后添加一位验证码。验证码的计算方案:

将前 17 位分别乘以对应系数 (7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2) 并相加,然后除以 11 取余数,0-10 分别对应 1 0 x 9 8 7 6 5 4 3 2。

请编写一个程序,用户输入15位身份证号码,程序生成18位身份证号码。假设所有要升级的身份证的四位年份都是19××年

输入格式

一个15位的数字串,作为身份证号码

输出格式

一个18位的字符串,作为升级后的身份证号码

样例输入

110105491231002

样例输出

11010519491231002x

数据规模和约定

不用判断输入的15位字符串是否合理

解题思路:
根据题目意思代码模拟一下升级身份证的过程即可。

#include
using namespace std;

int main()
{
	char a[18];
    string s;
    while(cin>>s){
        for(int i=0;i<8;i++) 
			a[i]=s[i];
        a[6]='1';a[7]='9';
        for(int i=8;i<17;i++) a[i]=s[i-2];
        int sum=0;
        int b[17]={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
        for(int i=0;i<17;i++){
            sum+=int (a[i]-'0')*b[i];
        }
			
        char c[11]={'1','0','x','9','8','7','6','5','4','3','2'};
        for(int i=0;i<11;i++){
            if(sum%11==i){
                a[17]=c[i];
                break;
            }
        }
        for(int i=0;i<18;i++)cout<

D 试题 算法提高 排列数

难度:中 题目类型:数学-排列组合 / DFS

提交此题 评测记录

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

0、1、2三个数字的全排列有六种,按照字母序排列如下:

012、021、102、120、201、210

输入一个数n

求0~9十个数的全排列中的第n个(第1个为0123456789)。

输入格式

一行,包含一个整数n

输出格式

一行,包含一组10个数字的全排列

样例输入

1

样例输出

0123456789

数据规模和约定

0 < n <= 10!

解题思路:
第一种方法使用:C++内置的next_permutation函数,即可得到全排列。
第二种方法使用dfs去求排列数。

#include
using namespace std;
int main(){
    int num[10]={0,1,2,3,4,5,6,7,8,9};
    //sort(num,num+10);因为这里输入的数组本来就是排序好的 所以这里不需要再排序一次了
    long long n=0;
    cin>>n; 
    do{
        n--;
        if(n==0){
            for(int i=0;i<10;i++){
                cout<

E 试题 算法提高 P1001

难度:中 题目类型:基本算法-模拟 / 高精度乘法

提交此题 评测记录

资源限制

时间限制:1.0s 内存限制:256.0MB

当两个比较大的整数相乘时,可能会出现数据溢出的情形。为避免溢出,可以采用字符串的方法来实现两个大数之间的乘法。具体来说,首先以字符串的形式输入两个整数,每个整数的长度不会超过8位,然后把它们相乘的结果存储在另一个字符串当中(长度不会超过16位),最后把这个字符串打印出来。例如,假设用户输入为:62773417和12345678,则输出结果为:774980393241726.

输入:

62773417 12345678

输出:

774980393241726

解题思路:
方法一:因为这道题结果是16位不会超出long long 的范围可以直接longlong的两个数相乘即可。
方法二:是转换成字符串或者数组模拟乘法的过程。
方法三:还有一种方法是不转换成字符串或者数组的拆分数字做法,原理如下:2020年蓝桥杯暑假第3次练习赛(C++组)_第2张图片
方法一代码:


#include 
using namespace std;
 
int main(int argc, char *argv[]) {
	long long a,b;
	cin>> a>> b;
	cout<< a*b;
	return 0;
}

方法二代码:

#include
using namespace std;

const int N=10001;
int res[N*2], a[N], b[N];
string sa,sb;

int main() {
	cin>>sa>>sb;
		if(sa=="0"||sb=="0"){//没有这个的话最后一组用例无法通过 
			cout<<0;
			return 0;
		}
	int lenA = 0, lenB = 0;
	for(int i = sa.size()-1; i >= 0; i--) 
		a[lenA++] = sa[i]-'0';
	for(int i = sb.size()-1; i >= 0; i--) 
		b[lenB++] = sb[i]-'0';	
	for(int i = 0; i < lenA; i++)
		for(int j = 0; j < lenB; j++)
			res[i+j] += a[i]*b[j]; 	
			
	for(int i = 0; i < lenA+lenB; i++){
		if(res[i] >= 10){//进位 
			res[i+1] += res[i]/10;
			res[i] %= 10; 
		} 
	}
	
	int len = lenA+lenB-1;
 	if(res[len] != 0) cout<= 0; i--) cout<

F 试题 历届试题 买不到的数目

难度:高 题目类型:数学-扩展欧几里得 / 动态规划

提交此题 评测记录

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。

小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。

你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。

本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。

输入格式

两个正整数,表示每种包装中糖的颗数(都不多于1000)

输出格式

一个正整数,表示最大不能买到的糖数

样例输入1

4 7

样例输出1

17

样例输入2

3 5

样例输出2

7

解题思路:
第一种方法:扩展欧几里得
自然数a,b互质,则不能表示成ax+by(x,y为非负整数)的最大整数是ab-a-b.
而不互质的自然数,没有
证明:
a或者b是1的情况下容易证明.
以下情况都是a>1且b>1的情况.
首先证明ab-a-b不能表示成ax+by
假设ab-a-b=ax+by,那么ab=am+bn (m,n都大于等于1)
左边是a的倍数,右边am是a的倍数,那么要求bn也要是a的倍数
b不是a的倍数,只能要求n是a的倍数,这样的话,bn=bn’a>=ba
那么am=ab-bn<=0就与am>1矛盾.

第二种方法 动态规划
假如两个整数中最小的是min,最大的是max,那么我们知道一个数是否可以由输入的两个整数组成就看 当前数减去min或者减去max是否可以由输入的俩个数组成
设如果可以由输入的两个数组成那么dp[i]=1,否则为0。
那么就有***if(dp[i-max]==1||dp[i-min]==1) dp[i]=1***。
从这里我们就可以知道如果有连续的min个数可以由这两个数组成
那么后面的所有数都可以由其组成这是很显然的。

方法一代码:

#include
using namespace std;

int main()
{
    int a,b;
    cin>>a>>b;
    cout<

方法二代码:

#include
using namespace std;
int main(){
	int n,m;
   cin>>n>>m;
	int lmax=max(n,m);
	int lmin=min(n,m);
	int i=lmax+1;
	int dp[100001]; //建立一个dp数组   
	memset(dp,0,sizeof(dp));
	dp[lmax]=dp[lmin]=1;  //初始化 dp[lmax] dp[lmin] 
	int ans=0;   //记录可以由两个正整数组成的数的个数(连续的若中间出现了不满足的则重置为0) 
	while(i){
		if(dp[i-lmax]==1||dp[i-lmin]==1) {// 如果有个一满足那就 dp[i]=1; 
			dp[i]=1;
			ans++;
		}
		else {//如果中间 出现不满足的 那就 重置ans 
			ans=0;
		}
		if(ans==lmin)break;//如果ans==lmin  那i后面的所有数都可以由俩个数组成 
		i++;
	}
	int l=0;
	for(int j=0;j<=i;j++){
		if(dp[j]==0){
			l=j;//寻找最大的买不到的数目 
		};
	}
	cout<

你可能感兴趣的:(蓝桥杯,学习,ACM)