第六届蓝桥杯大赛个人赛省赛真题C++A组,B组,C组真题解析
文章目录
题目链接
A组真题
题目结构
| 题目 | 类型 | 分值 | 
|---|---|---|
| 第一题 | 结果填空 | 3分 | 
| 第二题 | 结果填空 | 5分 | 
| 第三题 | 结果填空 | 9分 | 
| 第四题 | 代码填空 | 11分 | 
| 第五题 | 代码填空 | 15分 | 
| 第六题 | 结果填空 | 17分 | 
| 第七题 | 结果填空 | 21分 | 
| 第八题 | 程序设计 | 13分 | 
| 第九题 | 程序设计 | 25分 | 
| 第十题 | 程序设计 | 31分 | 
第一题 方程整数解
-  问题重现 方程: a 2 + b 2 + c 2 = 1000 a^2 + b^2 + c^2 = 1000 a2+b2+c2=1000 
 (或参见下图)
  
 这个方程有整数解吗?有:a,b,c=6,8,30 就是一组解。
 你能算出另一组合适的解吗?
 请填写该解中最小的数字。
 注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
-  解题思路 
 我们是要找解中最小的数字。注意这里的 a , b , c a,b,c a,b,c都是整数,也就是说可以为负数,所以说我们可以看成是正数,得到其中最大的正数,取其负值即是我们的答案。这个正数我们可以直接模拟从大到小得到。
/*
*blog:https://blog.csdn.net/hzf0701
*邮箱:unique_powerhouse@qq.com
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*/
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
const int maxn=1e5;//数组所开最大值
const int mod=1e9+7;//模
const int inf=0x3f3f3f3f;//无穷大
void solve(){
    //我们可以模拟这个最小的数字。
    int temp;
    per(i,32,1){
        rep(j,1,32){
            temp=sqrt(1000-i*i-j*j);
            if(temp*temp+i*i+j*j==1000){
                cout<<(-1)*i<<endl;
                return;
            }
        }
    }
}
int main(){
    solve();
    return 0;
}
- 答案
 − 30 -30 −30
第二题 星系炸弹
-  问题重现 在X星系的广袤空间中漂浮着许多X星人造“炸弹”,用来作为宇宙中的路标。 
 每个炸弹都可以设定多少天之后爆炸。
 比如:阿尔法炸弹 2015 2015 2015年 1 1 1月 1 1 1日放置,定时为 15 15 15天,则它在 2015 2015 2015年 1 1 1月 16 16 16日爆炸。
 有一个贝塔炸弹, 2014 2014 2014年 11 11 11月 9 日 9日 9日放置,定时为 1000 1000 1000天,请你计算它爆炸的准确日期。
 请填写该日期,格式为 y y y y − m m − d d yyyy-mm-dd yyyy−mm−dd 即 4 4 4位年份 2 2 2位月份 2 2 2位日期。比如:2015-02-19
 注意:请严格按照格式书写。不能出现其它文字或符号。
-  解题思路 
 我们可以采用日期模拟的方式一个月一个月过渡完。 对于一些平闰年的细节我们要考虑到,用一个 m o n t h s months months数组来存储每个月的天数。要注意年份月份进位。具体看代码。这个题目也可以投机取巧来解决。笨的方法就是打开日历统计,还有一个特别巧的方法就是利用EXCEL来快捷得出答案。在A1中输入:2014-11-9(改为日期格式),在B1中输入:=A1+1000(同样单元格格式改为日期格式),则可得出结果。见下图。
  
-  代码 
/*
*blog:https://blog.csdn.net/hzf0701
*邮箱:unique_powerhouse@qq.com
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*/
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
const int maxn=1e5;//数组所开最大值
const int mod=1e9+7;//模
const int inf=0x3f3f3f3f;//无穷大
//模拟日期。
int months[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
int year,month,day;
int days=1000;
bool is_special(int year){
    if((year%4==0&&year%100!=0)||year%400==0){
        return true;
    }
    else{
        return false;
    }
}
void solve(){
    year = 2014,month=11,day=9;
    if(is_special(year)){
        months[2]=29;
    }
    else{
        months[2]=28;
    }
    while(days+day>months[month]){
        days-=(months[month]-day+1);
        day=1;
        month+=1;
        if(month>12){
            year+=1;
            month-=12;
        }
        if(is_special(year)){
            months[2]=29;
        }
        else{
            months[2]=28;
        }
    }
    day+=days;
    cout<<year<<"-"<<month<<"-"<<day<<endl;
}
int main(){
    solve();
    return 0;
}
- 答案
 2017 − 08 − 05 2017-08-05 2017−08−05
第三题 奇妙的数字
-  问题重现 小明发现了一个奇妙的数字。它的平方和立方正好把0~9的10个数字每个用且只用了一次。 
 你能猜出这个数字是多少吗?
 注意:请填写该数字,不要填写任何多余的内容。
-  解题思路 
 我们可以直接暴力枚举这个数字,根据条件来判断是否符合,所以我们可以设一个统计 0 0 0~ 9 9 9数量的数组 c n t cnt cnt,这样有利于我们判断条件。注意在每次枚举判断的时候都要将 c n t cnt cnt数组初始化。
-  代码 
/*
*blog:https://blog.csdn.net/hzf0701
*邮箱:unique_powerhouse@qq.com
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*/
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
const int maxn=1e5;//数组所开最大值
const int mod=1e9+7;//模
const int inf=0x3f3f3f3f;//无穷大
int cnt[10],temp1,temp2;
bool check(){
    rep(i,0,9){
        if(cnt[i]!=1){
            return false;
        }
    }
    return true;
}
void solve(){
    for(int i=1;;i++){
        memset(cnt,0,sizeof(cnt));
        temp1=pow(i,2),temp2=pow(i,3);
        while(temp1>0){
            cnt[temp1%10]++;
            temp1/=10;
        }
        while(temp2>0){
            cnt[temp2%10]++;
            temp2/=10;
        }
        if(check()){
            cout<<i<<endl;
            return;
        }
    }
}
int main(){
    solve();
    return 0;
}
- 答案
 69 69 69
第四题 格子中输出
- 问题重现 
  StringInGrid函数会在一个指定大小的格子中打印指定的字符串。
 要求字符串在水平、垂直两个方向上都居中。
 如果字符串太长,就截断。
 如果不能恰好居中,可以稍稍偏左或者偏上一点。
 下面的程序实现这个逻辑,请填写划线部分缺少的代码。
 对于题目中数据,应该输出:
  
 注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。
#include <stdio.h>
#include <string.h>
void StringInGrid(int width, int height, const char* s)
{
	int i,k;
	char buf[1000];
	strcpy(buf, s);
	if(strlen(s)>width-2) buf[width-2]=0;
	
	printf("+");
	for(i=0;i<width-2;i++) printf("-");
	printf("+\n");
	
	for(k=1; k<(height-1)/2;k++){
		printf("|");
		for(i=0;i<width-2;i++) printf(" ");
		printf("|\n");
	}
	
	printf("|");
	
	printf("%*s%s%*s",_____________________________________________);  //填空
	          
	printf("|\n");
	
	for(k=(height-1)/2+1; k<height-1; k++){
		printf("|");
		for(i=0;i<width-2;i++) printf(" ");
		printf("|\n");
	}	
	
	printf("+");
	for(i=0;i<width-2;i++) printf("-");
	printf("+\n");	
}
int main()
{
	StringInGrid(20,6,"abcd1234");
	return 0;
}
-  解题思路 
 很明显,这个是一个简单的打印程序,所以我们其实只要关心我们要打印的中心内容,即我们要填的空。在填这个空之前,我们必须要知道格式字符串的概念。%[*][输入数据宽度][长度]类型。尤其是'*'。在scanf中使用,则添加了*的部分会被忽略,不会被参数获取。 在printf中使用,表示用后面的形参代替的位置,实现动态格式输出。例如:printf("%*s",10,s);意思是输出字符串s,但至少占10个位置。不足的在字符串s左边补空格。(记住是左边。) 所以,在printf("%*s%s%*s",_____________________________________________);中,中间的%s自然是输出字符串buf,所以我们左右边的%*s自然是作为填充空格用的,那么我们应该填充多少个空格呢?那自然是根据所剩宽度和字符串长度决定的。即 ( w i d t h − 2 − s t r l e n ( b u f ) ) (width-2-strlen(buf)) (width−2−strlen(buf)),那么我们对这个进行左右个一半除以 2 2 2,即可得我们需要填充的空格为: ( w i d t h − s t r l e n ( b u f ) ) / 2 − 1 (width-strlen(buf))/2-1 (width−strlen(buf))/2−1。由于空格已经填充完了,那么我们输出的自然是“”,避免占用空间。
-  答案 
(width-strlen(buf))/2-1,"",buf,(width-strlen(buf))/2-1,""
第五题 九数组分数
- 问题重现 
  1 , 2 , 3 … 9 1,2,3…9 1,2,3…9这九个数字组成一个分数,其值恰好为 1 / 3 1/3 1/3,如何组法? 
 下面的程序实现了该功能,请填写划线部分缺失的代码。
 注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。
#include <stdio.h>
void test(int x[])
{
	int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
	int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
	
	if(a*3==b) printf("%d / %d\n", a, b);
}
void f(int x[], int k)
{
	int i,t;
	if(k>=9){
		test(x);
		return;
	}
	
	for(i=k; i<9; i++){
		{t=x[k]; x[k]=x[i]; x[i]=t;}
		f(x,k+1);
		_____________________________________________ // 填空处
	}
}
	
int main()
{
	int x[] = {1,2,3,4,5,6,7,8,9};
	f(x,0);	
	return 0;
}
-  解题思路 
 如果经常使用 d f s dfs dfs回溯的就很明白这里应该填什么了。对于每条路径我们去搜索后,我们必须还原回来,不能影响到其他的同状态路径。
-  答案 
{t=x[k];x[k]=x[i];x[i]=t;}
第六题 牌型种数
-  问题重现 小明被劫持到X赌城,被迫与其他3人玩牌。 
 一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
 这时,小明脑子里突然冒出一个问题:
 如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?
 注意:请填写该整数,不要填写任何多余的内容或说明文字。
-  解题思路 
 对于这种问题,我们可以暴力解决,但明显复杂,并且很容易出错。那么这个时候就要想到我们的dfs回溯来写了。首先我们确定得好递归出入口,即初始状态是从0开始,表示我们手里并没有任何牌,那么结束状态也就是当我们手上有13张牌的时候。 同样,为了不重复计算,所以我们要按顺序选牌,我们可以给牌编号为1~13,那么我们从1号开始递归选择。
-  代码 
/*
*blog:https://blog.csdn.net/hzf0701
*邮箱:unique_powerhouse@qq.com
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*/
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
const int maxn=1e5;//数组所开最大值
const int mod=1e9+7;//模
const int inf=0x3f3f3f3f;//无穷大
int ans;//统计方案数。
void dfs(int index,int sum){
    if(sum==13){
        ans++;
        return;
    }
    if(index==14||sum>13){
        //越界了。
        return;
    }
    //拿每种卡牌的0~4张。
    rep(i,0,4){
        dfs(index+1,sum+i);
    }
}
void solve(){
    ans=0;
    dfs(1,0);
    cout<<ans<<endl;
}
int main(){
    solve();
    return 0;
}
- 答案
 3598150 3598150 3598150
第七题 手链样式
-  问题重现 小明有3颗红珊瑚,4颗白珊瑚,5颗黄玛瑙。 
 他想用它们串成一圈作为手链,送给女朋友。
 现在小明想知道:如果考虑手链可以随意转动或翻转,一共可以有多少不同的组合样式呢?
 注意:请你提交该整数。不要填写任何多余的内容或说明性的文字。
-  解题思路 
 首先我们在解这道题的时候一定要理解题意,转动和翻转到底是一个什么意思?转动就是我们所得到的排列是一个环。而翻转就代表这个排列是一个立体的,可以上下左右翻转。我们可以看一个例子。
  
 这四种组合其实都是一样的,弄清楚了题意,我们才能继续往下。我们可以把红珊瑚看做 a a a,白珊瑚看做 b b b,黄珊瑚看做 c c c,那么这个手链其实我们就可以表示为:"aaabbbbccccc"(只是列举一个排列。),那么对于字符串而言,我们就可以使用STL中的全排列函数来实现组合。实现了排列组合,其实就好办了,为了实现转动,我们存储字符串时需要将其复制一份,形成环状。例如abcd,即变为abcdabcd。而翻转的话,我们可以用reverse函数来实现。上述问题解决完了,这道题其实就好办了,我们存储字符串可以使用vector或者set容器,每次生成字符串时都应遍历其中的字符串,判断生成字符串是否包含在里面。
-  代码 
/*
*blog:https://blog.csdn.net/hzf0701
*邮箱:unique_powerhouse@qq.com
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*/
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
const int maxn=1e5;//数组所开最大值
const int mod=1e9+7;//模
const int inf=0x3f3f3f3f;//无穷大
//这个时候是给定了的,我们可以利用字符串来形式表示。即"aaabbbbccccc";
void solve(){
    set<string> v;
    int sum=0;
    string str="aaabbbbccccc";//升序。
    do{
        auto iter = v.begin();
        for(;iter!=v.end();iter++){
            if((*iter).find(str)!=string::npos){
                //判断是否包含字符串str;
                break;
            }
        }
        if(iter!=v.end()){
            //说明迭代器没有到达最后一个元素,即包含字符串str了。
            continue;
        }
        string temp=str+str;//构成环形,实现旋转的效果,即排除因旋转而重复的字符串。
        v.insert(temp);
        reverse(temp.begin(),temp.end());//实现反转,排除反转的情况。
        v.insert(temp);
        sum++;
    }while(next_permutation(str.begin(),str.end()));//str全排列。
    cout<<sum<<endl;
}
int main(){
    solve();
    return 0;
}
- 答案
 1170 1170 1170
第八题 饮料换购
-  问题重现 乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去(但不允许暂借或赊账)。 
 请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的n瓶饮料,最后他一共能喝到多少瓶饮料。
 输入格式
 一个整数n,表示开始购买的饮料数量(0<n<10000)
 输出格式
 一个整数,表示实际得到的饮料数
 样例输入
 100
 样例输出
 149
 样例输入
 101
 样例输出
 151
 资源约定
 峰值内存消耗 < 256M
 CPU消耗 < 1000ms
 注意
 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
 所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
 main函数需要返回0
 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
 提交时,注意选择所期望的编译器类型。
-  解题思路 
 这道题相对比较简单,我们首先要分清楚瓶盖数和饮料数,这两者不能混淆。那么我们设置变量:用n来表示剩余瓶盖数,result表示我们实际得到的数,temp表示我们当时兑换获得的饮料。 在初始条件下,易知result=n。在每次兑换的时候,我们要注意变量之间的增减关系。
-  代码 
/*
*blog:https://blog.csdn.net/hzf0701
*邮箱:unique_powerhouse@qq.com
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*/
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
using namespace std;
typedef long long ll;
const int maxn=1e5;//数组所开最大值
const int mod=1e9+7;//模
const int inf=0x3f3f3f3f;//无穷大
int n,result,temp;
void solve(){
    //我们用n来表示剩余瓶盖数,result表示我们实际得到的数,temp表示我们当时兑换获得的饮料。
    result=n;
    while(n>=3){
        temp=n/3;
        n-=temp*3;
        result+=temp;
        n+=temp;
    }
    cout<<result<<endl;
}
int main(){
    cin>>n;
    solve();
    return 0;
}
第九题 垒骰子
-  问题重现 赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。 
 经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
 我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
 假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。
 atm想计算一下有多少种不同的可能的垒骰子方式。
 两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
 由于方案数可能过多,请输出模 1 e 9 + 7 1e9+7 1e9+7 的结果。
 输入格式
 第一行包含两个整数 n,m,分别表示骰子的数目和排斥的组数。
 接下来 m 行,每行两个整数 a,b,表示 a 和 b 数字不能紧贴在一起。
 输出格式
 共一个数,表示答案模 109+7 的结果。
 数据范围
 1 ≤ n ≤ 109 , 1≤n≤109, 1≤n≤109,
 1 ≤ m ≤ 36 , 1≤m≤36, 1≤m≤36,
 1 ≤ a , b ≤ 6 1≤a,b≤6 1≤a,b≤6
 输入样例:
 21 2 1 21
 12 1 2 12
 输出样例:
 544 544 544
-  解题思路 
 对于这个题目,我们首先要清楚骰子的摆放,由于任意一面朝上的侧面都可以旋转四次,所以一个骰子的摆放方法有 24 24 24种。为了接下来的处理,我们先定义状态,用 f i j f_{ij} fij表示已经放置了前 i i i个骰子且最上面那个是第 i i i个骰子其最上面是值为 j j j的面的方案数,用 h i ( o p [ j ] ) h_{i(op[j])} hi(op[j])表示值为 i i i的面与 o p [ j ] op[j] op[j]的面(因为我们都是考虑上面,表示的也都是上面,所以为了方便,我们都用上面来表示,而下面我们可以用一个数组 o p op op来对应,自然可以用 o p [ j ] op[j] op[j]来表示。)是否互斥,若其值为1说明互斥,反之不互斥。 那么不难发现 f i j = ∑ k = 1 n 4 × f ( i − 1 ) k × h i ( o p [ j ] ) f_{ij}=\sum_{k=1}^{n}{4\times f_{(i-1)k}\times h_{i(op[j])}} fij=∑k=1n4×f(i−1)k×hi(op[j])(前面已经解释了,骰子可以沿着侧面旋转,故需 × 4 \times4 ×4)。为了方便,接下来我们再规定一个函数 F i = ∑ j = 1 6 f i j F_i=\sum_{j=1}^6{f_{ij}} Fi=∑j=16fij,故此问题故是在求解 F n F_n Fn。而这就是明明白白的动态规划。初始状态为 F 1 F_1 F1,其不受限制,故方案数为 24 24 24。那么其实我们代码就已经可以写出来了。
for(int i=1;i<=n;i++){
    for(int j=1;j<6;j++){
        //对6个面的放置状态均进行转移。
        for(int k=1;k<6;k++){
            if(!h[j][op[k]]){
                f[i][j]=(f[i][j]+f[i-1][k]*4)%mod
            }
        }
    }
}
这样,我们这个题目是不是解决完了呢?我们发现题目中 n n n的范围是 1 e 9 1e9 1e9,这样算下来复杂度达到了 1 e 10 1e10 1e10,这明显会超时,所以我们得进行优化。我们现在用数组来表示,由于:
F n = f [ n ] [ 1 ] + f [ n ] [ 2 ] + f [ n ] [ 3 ] + f [ n ] [ 4 ] + f [ n ] [ 5 ] + f [ n ] [ 6 ] F_n={f[n][1]+f[n][2]+f[n][3]+f[n][4]+f[n][5]+f[n][6]} Fn=f[n][1]+f[n][2]+f[n][3]+f[n][4]+f[n][5]+f[n][6]
F n − 1 = f [ n − 1 ] [ 1 ] + f [ n − 1 ] [ 2 ] + f [ n − 1 ] [ 3 ] + f [ n − 1 ] [ 4 ] + f [ n − 1 ] [ 5 ] + f [ n − 1 ] [ 6 ] F_{n-1}={f[n-1][1]+f[n-1][2]+f[n-1][3]+f[n-1][4]+f[n-1][5]+f[n-1][6]} Fn−1=f[n−1][1]+f[n−1][2]+f[n−1][3]+f[n−1][4]+f[n−1][5]+f[n−1][6]
而这两个像不像一个 n n n行 1 1 1列的矩阵,我们就把它们当做矩阵,那么自然可以假设 F n − 1 × A = F n F_{n-1}\times A = F_{n} Fn−1×A=Fn。由矩阵乘法可知, A A A是一个 6 6 6行 6 6 6列的矩阵。所以我们最关键的就是找出这个矩阵。我们仔细观察一下上面的代码,这和我们的矩阵运算有什么区别吗?很显然,这就是在和我们的 A A A进行矩阵运算,那么我们这里就可以列出我们矩阵A的构造方法了。如下:
int a[7][7];
for(int i=1;i<7;i++){
    for(int j=1;j<7;j++){
        if(h[i][op[j]])a[i][j]=0;
        else a[i][j]=4;
        
    }
}
构造出矩阵A后,这个题目就变得简单多了,因为 F n = F 1 × A n − 1 F_n=F_{1}\times A^{n-1} Fn=F1×An−1,而 F 1 = ( 4 , 4 , 4 , 4 , 4 , 4 ) F_1=(4,4,4,4,4,4) F1=(4,4,4,4,4,4)。我们最后要求的是总方案数,其实也就是将最后得到的矩阵幂各个位置 × 4 \times 4 ×4再累加起来。这就是答案。
- 代码
/**
 *@author: pursuit
 *@email: 2825841950@qq.com
 *@created: 2021-03-13-16:03
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const ll mod = 1e9+7;
const int N = 7;
bool h[N][N];
int op[N]={0,4,5,6,1,2,3};
int n,m;
struct mat{
    ll A[N][N];
    mat(){
        memset(A,0,sizeof(A));
    }
    //等于这里必须使用引用。
    mat&  operator=(const mat &arg){
        for(int i=1;i<N;i++){
            for(int j=1;j<N;j++){
                A[i][j]=arg.A[i][j];
            }
        }
    }
    mat operator*(const mat &arg){
        mat temp;//临时矩阵,存储运算结果。
        //矩阵相乘,A矩阵的第i行乘以B矩阵的第j列。
        for(int i=1;i<N;i++){
            //代表第i行
            for(int j=1;j<N;j++){
                //代表第j列。
                for(int k=1;k<N;k++){
                    temp.A[i][j]=(temp.A[i][j]+arg.A[i][k]*A[k][j])%mod;
                }
            }
        }
        return temp;
    }
};
mat init_e(){
    mat temp;
    //初始化为单位矩阵。
    for(int i=1;i<N;i++){
        for(int j=1;j<N;j++){
            if(i==j){
                temp.A[i][j]=1;
            }
        }
    }
    return temp;
}
mat quick_pow(mat A,int q){
    mat ans = init_e();
    while(q){
        if(q&1)ans=ans*A;
        A=A*A;
        q>>=1;
    }
    return ans;
}
mat res,ans;//代表我们的矩阵A。
void solve(){
    ll result=0;
    ans=quick_pow(res,n-1);
    for(int i=1;i<N;i++){
        for(int j=1;j<N;j++){
            result=(result+4*ans.A[i][j])%mod;
        }
    }
    cout<<result<<endl;
}
int main() {
    cin>>n>>m;
    int a,b;
    for(int i=0;i<m;i++){
        cin>>a>>b;
        h[a][b]=h[b][a]=true;
    }
    for(int i=1;i<N;i++){
        for(int j=1;j<N;j++){
            if(!h[i][op[j]]){
               res.A[i][j]=4; 
            }
        }
    }
    solve();
    return 0;
}
第十题 灾后重建
暂时不会,离谱的是网上代码放测试平台上都没有通过,等日后再补上。
B组真题(其余题目和A组题目相同)
第一题 奖券数目
- 问题重现 
  有些人很迷信数字,比如带“4”的数字,认为和“死”谐音,就觉得不吉利。 
 虽然这些说法纯属无稽之谈,但有时还要迎合大众的需求。某抽奖活动的奖券号码是5位数(10000-99999),要求其中不要出现带“4”的号码,主办单位请你计算一下,如果任何两张奖券不重号,最多可发出奖券多少张。
 请提交该数字(一个整数),不要写任何多余的内容或说明性文字。
- 解题思路
 这道题为组合数学,即每一位上都不能出现 4 4 4,那么易知奖券数为: C 8 1 × C 9 1 × C 9 1 × C 9 1 × C 9 1 = 52488 C_{8}^{1}\times C_{9}^{1}\times C_{9}^{1}\times C_{9}^{1}\times C_{9}^{1}=52488 C81×C91×C91×C91×C91=52488。
- 答案
 52488 52488 52488
第三题 祥瑞生辉
- 问题重现 
  观察下面的加法算式: 
  
 其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。
 请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。
- 解题思路
 直接暴力枚举判断,该题目中有8个未知数,那么我们完全可以定义一个num数组来存储0->9,按字典升序,假定祥瑞生辉三羊献气对应 n u m [ 0 ] − > n u m [ 7 ] num[0]->num[7] num[0]−>num[7],之后全排列构造得到所有的情况。
- 代码
/**
* @filennumme:三羊献瑞.cbp
* @Author : pursuit
* @Blog:unique_pursuit
* @email: 2825841950@qq.com
* @Date : 2021-03-14-13.20.01
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn=1e5+5;
const int mod=1e9+7;
//直接暴力枚举判断。其中出现了8个数字,我们枚举它们的值即可。
//我们定义一个num数组来存储0->9,按字典升序,之后全排列构造得到所有的情况。
int num[]={0,1,2,3,4,5,6,7,8,9};
void solve(){
    //我们定义祥瑞生辉三羊献气对应num[0]->num[7]。
    int sum1,sum2,sum3;
    do{
        //由于三和祥是首位,不能为0.
        if(num[0]==0||num[4]==0)continue;
        sum1=num[0]*1000+num[1]*100+num[2]*10+num[3];
        sum2=num[4]*1000+num[5]*100+num[6]*10+num[1];
        sum3=num[4]*10000+num[5]*1000+num[2]*100+num[1]*10+num[7];
        if(sum1+sum2==sum3){
            cout<<sum2<<endl;
            break;
        }
    }while(next_permutation(num,num+10));
}
int main(){
    solve();
    return 0;
}
- 答案
 1085 1085 1085。
第六题 加法变乘法
- 问题重现 
  我们都知道: 1 + 2 + 3 + . . . + 49 = 1225 1+2+3+ ... + 49 = 1225 1+2+3+...+49=1225 
 现在要求你把其中两个 不相邻的加号变成乘号,使得结果为2015
 比如:
 1 + 2 + 3 + . . . + 10 ∗ 11 + 12 + . . . + 27 ∗ 28 + 29 + . . . + 49 = 2015 1+2+3+...+10*11+12+...+27*28+29+...+49 = 2015 1+2+3+...+10∗11+12+...+27∗28+29+...+49=2015
 就是符合要求的答案。
 请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交(对于示例,就是提交 10 10 10)。
 注意:需要你提交的是一个整数,不要填写任何多余的内容。
- 解题思路
 这个式子中总共有48个加号,而我们的目标是使其中两个不相邻的加号变为乘号,使得结果变为2015.所以我们完全可以枚举这两个加号的位置。要注意的一个就是如果我们进行加法那就是消去一个元素,如果我们进行乘法,那就是消去两个元素,同样我们也要知道运算符的序号和前面的数字相等,也就是我们其实就是在找第一个乘号的位置,注意排除样例。
- 代码
/**
* @filename:加法变乘法.cbp
* @Author : pursuit
* @Blog:unique_pursuit
* @email: 2825841950@qq.com
* @Date : 2021-03-14-13.46.29
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn=1e5+5;
const int mod=1e9+7;
void solve(){
    int first,second;//加号的位置。
    for(first=1;first<=48;first++){
        //由于需要不相邻,故第二个乘号和第一个距离为1.
        //排除第一个答案。
        if(first==10)continue;
        for(second=first+2;second<=48;second++){
            //枚举位置之后进行运算。
            int ans=0,i=1;
            while(i<=49){
                if((i==first||i==second)){
                    //说明此时i需要做的是乘法运算。
                    ans+=i*(i+1);
                    i+=2;
                }
                else{
                   ans+=i;
                   i++;
                }
            }
            if(ans==2015){
                cout<<first<<endl;
            }
        }
    }
}
int main(){
    solve();
    return 0;
}
- 答案
 16 16 16.
第八题 移动距离
- 问题重现 
  X星球居民小区的楼房全是一样的,并且按矩阵样式排列。其楼房的编号为 1 , 2 , 3... 1,2,3... 1,2,3... 
 当排满一行时,从下一行相邻的楼往反方向排号。
 比如:当小区排号宽度为6时,开始情形如下:
 1 2 3 4 5 6
 12 11 10 9 8 7
 13 14 15 …
 我们的问题是:已知了两个楼号m和n,需要求出它们之间的最短移动距离(不能斜线方向移动)
 输入为3个整数w m n,空格分开,都在1到10000范围内
 w为排号宽度,m,n为待计算的楼号。
 要求输出一个整数,表示m n 两楼间最短移动距离。
 例如:
 用户输入:
 6 8 2
 则,程序应该输出:
 4
 再例如:
 用户输入:
 4 7 20
 则,程序应该输出:
 5
 资源约定:
 峰值内存消耗 < 256M
 CPU消耗 < 1000ms
 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
 所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
 注意: main函数需要返回0
 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
 注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
 提交时,注意选择所期望的编译器类型。
- 解题思路
 这道题所说的距离不是欧几里得距离,而是曼哈顿距离,即 d i s t a n c e = ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ distance=|x_1-x_2|+|y_1-y_2| distance=∣x1−x2∣+∣y1−y2∣。(因为题目中规定了只能走水平和垂直方向)那么求解两个楼房的坐标即成为解题的关键。我们以 m m m为例,易知 x m = ( m − 1 ) % w + 1 x_m=(m-1)\%w+1 xm=(m−1)%w+1,这里加 1 1 1是因为想让其行序号是从 1 1 1开始的,那么对于列来说呢?题目中对应是按 S S S型来排的,观察易知为“奇顺偶逆”,所以我们必须对偶数行的进行特殊处理,即翻转列坐标。
- 代码
/**
* @filename:移动距离.cbp
* @Author : pursuit
* @Blog:unique_pursuit
* @email: 2825841950@qq.com
* @Date : 2021-03-14-14.16.24
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn=1e5+5;
const int mod=1e9+7;
//这道题的思路其实就是判断m和n之间隔了多少行多少列。
int w,m,n;
void solve(){
    //由于楼房排序为奇顺偶反,所以我们得先确定在第几行。对楼宽作除即可。
    int mx,my,nx,ny;
    mx=(m-1)/w+1,nx=(n-1)/w+1;//获取在第几行。
    //奇顺偶反。
    my=m%w;
    if(my==0)my=w;//接下来我们要判断是哪个方向。
    if(mx%2==0){
        my=w-my+1;
    }
    ny=n%w;
    if(ny==0)ny=w;//接下来我们要判断是哪个方向。
    if(nx%2==0){
        ny=w-ny+1;
    }
    cout<<abs(nx-mx)+abs(ny-my)<<endl;
}
int main(){
    while(cin>>w>>m>>n){
        solve();
    }
    return 0;
}
第十题 生命之树
本弱鸡还不会树形dp,本题待补。
C组真题(其余题目和A组B组题目相同)
第一题 隔行变色
- 问题重现 
  Excel表的格子很多,为了避免把某行的数据和相邻行混淆,可以采用隔行变色的样式。 
 小明设计的样式为:第1行蓝色,第2行白色,第3行蓝色,第4行白色,…
 现在小明想知道,从第21行到第50行一共包含了多少个蓝色的行。
 请你直接提交这个整数,千万不要填写任何多余的内容。
- 解题思路
 水题一道,统计奇数行。
- 答案
 15 15 15。
第二题 立方尾不变
- 问题重现 
  有些数字的立方的末尾正好是该数字本身。 
 比如:1,4,5,6,9,24,25,…
 请你计算一下,在 10000以内的数字中(指该数字,并非它立方后的数值),符合这个特征的正整数一共有多少个。
 请提交该整数,不要填写任何多余的内容。
- 解题思路
 暴力统计即可。这题的关键在于我们怎么获得立方的尾数,该获取多少位,这些是由我们的原来数字长度决定的,获得之后进行比较即可。
- 代码
/**
* @filename:立方尾不变.cbp
* @Author : pursuit
* @Blog:unique_pursuit
* @email: 2825841950@qq.com
* @Date : 2021-03-14-18.40.31
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn=1e5+5;
const int mod=1e9+7;
//暴力统计即可。答案为36.
ll pow_ll(ll n,ll q){
    ll result=1;
    while(q){
        if(q&1)result*=n;
        n*=n;
        q>>=1;
    }
    return result;
}
void solve(){
    int ans=0;
    for(ll i=1;i<10000;i++){
        //先确定i的长度,再根据其取模。
        ll temp=i,cnt=0;
        while(temp>0){
            cnt++;
            temp/=10;
        }
        if(i==(i*i*i)%(pow_ll(10,cnt))){
            ans++;
        }
    }
    cout<<ans<<endl;
}
int main(){
    solve();
    return 0;
}
- 答案
 36 36 36.
第五题 串逐位和
- 问题重现 
  给定一个由数字组成的字符串,我们希望得到它的各个数位的和。 
 比如:“368” 的逐位和是:17
 这本来很容易,但为了充分发挥计算机多核的优势,小明设计了如下的方案:
int f(char s[], int begin, int end)
{
int mid;
if(end-begin==1) return s[begin] - '0';
mid = (end+begin) / 2;
return ____________________________________;  //填空
}
int main()
{
char s[] = "4725873285783245723";
printf("%d\n",f(s,0,strlen(s)));
return 0;
}
你能读懂他的思路吗? 请填写划线部分缺失的代码。
注意:只填写缺少的部分,不要填写已有代码或任何多余内容。
- 解题思路
 我们首先要知道这个 f f f函数是在求什么?不难发现,是在求 [ b e g i n , e n d ) [begin,end) [begin,end)之间的数位和。再观察代码内部,其中设置了中点,而当区间长度为 1 1 1时返回该位的和,故此题是将当前区间一分为二,递归求和。知道了这些,其中填空就显而易见了,即分为左半区间 [ b e g i n , m i d ) [begin,mid) [begin,mid)和右半区间 [ m i d , e n d ) [mid,end) [mid,end)。
- 答案
f(s,begin,mid)+f(s,mid,end)
第九题 打印大X
-  问题重现 小明希望用星号拼凑,打印出一个大X,他要求能够控制笔画的宽度和整个字的高度。 
 为了便于比对空格,所有的空白位置都以句点符来代替。
 要求输入两个整数m n,表示笔的宽度,X的高度。用空格分开(0<m<n, 3<n<1000, 保证n是奇数)
 要求输出一个大X
 例如,用户输入:
 3 9
 程序应该输出:
  
 再例如,用户输入:
 4 21
 程序应该输出
  
 资源约定:
 峰值内存消耗 < 256M
 CPU消耗 < 1000ms
 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
 所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
 注意: main函数需要返回0
 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
 注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
-  解题思路 
 打印图形类的题目,我们通常是要寻找规律的。由题意知该图形的行数为 n n n,那么列数呢?我们观察最中间一行,其中有 m m m个“”,左右两边各有“."的个数为 n / 2 n/2 n/2,故我们可得其列数为 m + 2 ∗ ( n / 2 ) m+2*(n/2) m+2∗(n/2)。接下来就是绘图了,为了方便,我们先将我们的图案初始化为“.”,这样我们就只需要填充“”即可。而填充“*”我们可以利用滑动指针来解决了,用 l e f t left left和 r i g h t right right从第一行滑动到最后一行。(因为从上往下看发现笔在形象的移动)。
-  代码 
/**
* @filename:打印大X.cbp
* @Author : pursuit
* @Blog:unique_pursuit
* @email: 2825841950@qq.com
* @Date : 2021-03-14-19.38.59
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn=3000;
const int mod=1e9+7;
int m,n;//m表示笔的宽度,n表示大X的高度。
char paint[maxn][maxn];
void print(int row,int col){
    for(int i=0;i<row;i++){
        for(int j=0;j<col;j++){
            cout<<paint[i][j];
        }
        cout<<endl;
    }
}
void solve(){
    memset(paint,'.',sizeof(paint));
    int row=n,col=m+2*(n/2);
    //先对上半部分进行处理。左右两边都要进行,我们利用滑动指针进行。
    int left=0,right=col-1;
    for(int i=0;i<row;i++){
        for(int j=0;j<m;j++){
            //绘制笔的宽度即可。
            paint[i][left+j]='*';
            paint[i][right-j]='*';
        }
        left++,right--;
    }
    print(row,col);
}
int main(){
    while(cin>>m>>n){
        solve();
    }
    return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号