蓝桥杯-历届试题之大臣的旅费
最后更新于:2022-04-01 14:53:36
## 历届试题 大臣的旅费
时间限制:1.0s 内存限制:256.0MB
问题描述
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
城市从1开始依次编号,1号城市为首都。
接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
输出格式
输出一个整数,表示大臣J最多花费的路费是多少。
样例输入1
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
输出格式
大臣J从城市4到城市5要花费135的路费。
这道题,如果不看提示,感觉像是图论题(有点忒明显了),
但是,提示说是 深度优先遍历,恩,于是DFS做了。
打表存某点到某点的距离,不能到达则为-1.
这样DFS做 75分。。
YM用Floyed做也是75分。。。
最后一组数据据说是10000左右,
DFS打表是存不下这么大数组,超空间了。
等回头用别的方法试试,之前测试过一个JAVA程序,是可以过的,测试数据应该就没有问题了。
我的DFS(75分)
~~~
/********************
*********************
* Author:Tree *
*From :http://blog.csdn.net/lttree *
* Title : 大臣的旅费 *
*Source: 蓝桥杯 历届试题 *
* Hint : 深度优先遍历 *
*********************
********************/
#include <iostream>
#include <string.h>
using namespace std;
int n,val,Map[1001][1001];
bool vis[1001][1001];
void dfs(int i,int v)
{
int j;
for( j=1;j<=n;++j )
{
if( Map[i][j]==-1 || vis[i][j] ) continue;
vis[i][j]=vis[j][i]=1;
dfs( j,v+Map[i][j] );
vis[i][j]=vis[j][i]=0;
}
val= v>val?v:val;
}
int find_max( void )
{
int i,Max;
Max=-1;
for(i=1;i<=n;++i)
{
memset(vis,0,sizeof(vis));
val=-1;
dfs(i,0);
Max= val>Max?val:Max;
}
return Max;
}
int main()
{
int i,p,q,d,num,sum;
while( cin>>n )
{
memset(Map,-1,sizeof(Map));
for(i=1;i<n;++i)
{
cin>>p>>q>>d;
Map[p][q]=d;
Map[q][p]=d;
}
num=find_max();
if( num&1 ) sum= (num+1)*(num/2)+(num+1)/2;
else sum=(num*num+num)/2;
cout<<sum+num*10<<endl;
}
return 0;
}
~~~
那个100分能过的 JAVA代码(非本人所写)
~~~
import java.util.*;
public class Main {
public static void main(String args[]){
Scanner scan=new Scanner (System.in);
int n=scan.nextInt();
QiDian[] QiDians=new QiDian[n];
for(int i=0;i<n;i++){
QiDians[i]=new QiDian(i,new ArrayList<ZhongDian>());
}
int tem1=0;
int tem2=0;
int quanzhong=0;
for(int i=0;i<n-1;i++){
tem1=scan.nextInt();
tem2=scan.nextInt();
quanzhong=scan.nextInt();
QiDians[tem1-1].list.add(new ZhongDian(quanzhong,QiDians[tem2-1]));
QiDians[tem2-1].list.add(new ZhongDian(quanzhong,QiDians[tem1-1]));
}
int[] data=search(QiDians[0],null);
int[] data2=search(QiDians[data[1]],null);
int sum=0;
for(int i=1;i<=data2[0];i++){
sum+=i+10;
}
System.out.println(sum);
}
public static int[] search(QiDian q,QiDian p){
int[] data=new int[]{0,q.index};
for(int i=0;i<q.list.size();i++){
if(q.list.get(i).qidian.equals(p)==false){
int [] data2=search(q.list.get(i).qidian,q);
int tem=q.list.get(i).quanzhong+data2[0];
if(tem>data[0]){
data[0]=tem;
data[1]=data2[1];
}
}
}
return data;
}
}
class QiDian{
int index;
ArrayList<ZhongDian> list=new ArrayList<ZhongDian>();
public QiDian(int index, ArrayList<ZhongDian> list) {
super();
this.index = index;
this.list = list;
}
}
class ZhongDian{
int quanzhong;
QiDian qidian;
public ZhongDian(int quanzhong, QiDian qidian) {
super();
this.quanzhong = quanzhong;
this.qidian = qidian;
}
}
~~~
蓝桥杯-结果填空之排座位
最后更新于:2022-04-01 14:53:34
## 排座位
要安排:3个A国人,3个B国人,3个C国人坐成一排。
要求不能使连续的3个人是同一个国籍。
求所有不同方案的总数?
这道题,一看就排列组合的题目。。。据说给学校讲概率论老师做,老师纯手算,
费了好大劲。。。好吧,学计算机的当然不能浪费电脑的速度啦。。。
纯暴力破解吧!
我想法就是把三个国家A,B,C用数字1,2,3来表示,
刚开始我想就是用1,2,3来循环做,如果碰到相连的三个国家,就可以continue,选下一个数字代表的国家。
结果发现,计算出来的只有5000多种和答案28万多种,差好多。。。
后来,经过小帅点播提醒,发现,国家要用九个的情况,并不能直接就让后面的不能选。
我原来的想法代码(错的):
~~~
#include <iostream>
using namespace std;
// A,B,C用数字1,2,3代替
// arr 记录9个人状态,sum记录种类数
int arr[10],sum;
// num下标记录每个数字总数是否到3
int num[4]={0,0,0,0};
void find(int n)
{
if(n==10)
{
int j;
for(j=1;j<=9;++j)
cout<<arr[j]<<" ";
cout<<endl;
++sum;
return;
}
int i,k;
for(i=1;i<=3;++i)
{
if(n<3)
{
if(num[i]==3) continue;
arr[n]=i;
++num[i];
find(n+1);
--num[i];
}
if(arr[n-1]==arr[n-2] && arr[n-1]==i) continue;
if(num[i]==3) continue;
arr[n]=i;
++num[i];
find(n+1);
--num[i];
}
}
int main()
{
sum=0;
find(1);
cout<<sum<<endl;
return 0;
}
~~~
正确的代码:
~~~
#include <iostream>
using namespace std;
// arr为排列方式数组
int arr[11],sum;
// num为九个国家,前三个为A国设置为1,中间三个B国,设置为2,后面三个C国,设置为3
int num[11];
// a数组来表示,相应下标国家是否被选
int a[11];
void find(int n)
{
// 人数大于4是,查找是否有连着三个相同的
if(n>=4)
{
for(int i=1;i<=n-3;i++)
if(arr[i]==arr[i+1]&&arr[i+1]==arr[i+2])
return ;
}
// n大于9,表示有一种排列可行
if(n>=10)
{
sum++;
return ;
}
// 九次循环
for(int i=1;i<=9;i++)
{
if(num[i]==0)
continue;
num[i]=0;
arr[n]=a[i];
find(n+1);
num[i]=1;
}
}
int main()
{
sum=0;
// 赋初值1,表示都没有被选过
for(int i=1;i<=9;i++)
num[i]=1;
a[1]=a[2]=a[3]=1;a[4]=a[5]=a[6]=2;a[7]=a[8]=a[9]=3;
find(1);
cout<<sum<<endl;
return 0;
}
~~~
蓝桥杯-结果填空题
最后更新于:2022-04-01 14:53:31
微生物增殖—除去次方数—古堡算式—奇怪的比赛—欧拉与鸡蛋
①微生物增殖
假设有两种微生物 X 和 Y
X出生后每隔3分钟分裂一次(数目加倍),Y出生后每隔2分钟分裂一次(数目加倍)。
一个新出生的X,半分钟之后吃掉1个Y,并且,从此开始,每隔1分钟吃1个Y。
现在已知有新出生的 X=10, Y=89,求60分钟后Y的数目。
如果X=10,Y=90 呢?
本题的要求就是写出这两种初始条件下,60分钟后Y的数目。
题目的结果令你震惊吗?这不是简单的数字游戏!真实的生物圈有着同样脆弱的性质!也许因为你消灭的那只 Y 就是最终导致 Y 种群灭绝的最后一根稻草!
一道数学题哈,这道题,有个小技巧,因为有需要算0.5分钟的,很麻烦,所以不如都X2,总共为120,
每隔0.5分钟即为每隔1。
这样,这道题可以分析为:
X每隔6分裂一次,Y每隔4分裂一次。
新生X每隔1吃Y,此后每隔2吃Y。
求120后,Y的数目。
肯定不能拿手算啦。。好累的o(╯□╰)o,
可以仔细分析一下题目,可以用一个for循环模拟时间的增长,
那么就可以对4,6取模来判定X、Y的分裂时间。
最关键就是X什么时候吃Y,或许刚开始考虑的时候,你觉得要分新生X与以前的X,
可是,你仔细看就会发现不然,Y每隔4才分裂一次,而新生的X与老X吃Y时间是重合的。
所以,只要是奇数个时间段(以120为总和的时间段)就会吃Y。
可以画一个时间轴来更清晰的表示出来:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-22_576a44afb9071.jpg)
我们可以很清晰的看出,红蓝点会重合,也就是新生的X与以前的X吃Y的时间段是重合的,所以,就可以通过
判断是否为奇数断点来吃Y。
代码:
~~~
#include <iostream>
using namespace std;
int main()
{
int time;
int x=10,y=89; // 第二次把Y改成90就可以了
for(time=1;time<=120;++time)
{
if(time%2==1)
y-=x;
if(time%4==0)
y+=y;
if(time%6==0)
x+=x;
if(y<0) {y=0;break;}
}
cout<<y<<endl;
return 0;
}
~~~
②除去次方数
自然数的平方数是:1 4 9 16 25 …
自然数的立方数是:1 8 27 64 125 …
自然数的4次方数是:1 16 81256 …
…
这些数字都可以称为次方数。
1~10000中,去掉所有的次方数,还剩下多少个数字?
这题完全可以用筛法求素数的方式来计算哟,
就是注意一下,最大到多少次方,2的几次方大于10000呢?
众所周知,2^10=1024 -> 2^14>10000
代码:
~~~
#include <iostream>
#include <cmath>
#include <string.h>
using namespace std;
bool no[10001];
int main()
{
int i,j,k;
int num,sum;
memset(no,0,sizeof(no));
// j为次方数
for(j=2;j<=14;++j)
{
// i为底数
i=1;
k=pow(i++,j);
while(k<10001)
{
no[k]=1;
k=pow(i++,j);
}
}
// 查找
sum=0;
for(i=1;i<10001;++i)
if(no[i]==0)
++sum;
cout<<sum<<endl;
return 0;
}
~~~
③古堡算式
福尔摩斯到某古堡探险,看到门上写着一个奇怪的算式:
ABCDE * ? = EDCBA
他对华生说:“ABCDE应该代表不同的数字,问号也代表某个数字!”
华生:“我猜也是!”
于是,两人沉默了好久,还是没有算出合适的结果来。
请你利用计算机的优势,找到破解的答案。
把 ABCDE 所代表的数字写出来。
一个五位数,乘以一个数得到另一个五位数,而且各位数字是相反的。
从题目中可以提取出的信息:
①相乘前后均为一个五位数
②这五位数各位数字没有相同的
③乘的是一个1~9的数字(准确的说是0~9,但是乘以0直接滤了)
我的做法:
设置一个变量Num从10000开始循环到100000,然后乘以1~9后的数传到函数,与原来num判断。
因为是结果填空题,所以就没有怎么优化,判断五个数各不相等,我也只是让第一个数不与后面的数相等。
这样就已经可以筛选的剩下一个了。。。
代码:
~~~
#include <iostream>
#include <string.h>
using namespace std;
int n[5];
void show(int num)
{
int i,temp;
temp=num;
for(i=0;i<5;++i)
{
if(num%10!=n[i]) return;
num/=10;
}
if(n[0]==n[1] || n[0]==n[2] || n[0]==n[3] || n[0]==n[4]) return;
cout<<temp<<endl;
}
int main()
{
int num,i,k,no;
for(num=10000;num<100000;++num)
{
for(k=1;k<10;++k)
{
memset(n,0,sizeof(n));
no=k*num;
if(no>100000) continue;
for(i=4;i>=0;--i)
{
n[i]=no%10;
no/=10;
}
show(num);
}
}
return 0;
}
~~~
④奇怪的比赛
某电视台举办了低碳生活大奖赛。题目的计分规则相当奇怪:
每位选手需要回答10个问题(其编号为1到10),越后面越有难度。答对的,当前分数翻倍;答错了则扣掉与题号相同的分数(选手必须回答问题,不回答按错误处理)。
每位选手都有一个起步的分数为10分。
某获胜选手最终得分刚好是100分,如果不让你看比赛过程,你能推断出他(她)哪个题目答对了,哪个题目答错了吗?
如果把答对的记为1,答错的记为0,则10个题目的回答情况可以用仅含有1和0的串来表示。例如:0010110011就是可能的情况。
你的任务是算出所有可能情况。每个答案占一行。
多个答案顺序不重要。
这道题,可以暴力的。。。用10个变量,for循环。。。
我用的是模拟二进制加法,一直往最后一位加1,再判断各位,如果有等于2的就进位。
一直到下标为0的内容为1,则退出。。
对了,还要注意,题目中讲的是,如果答对该题目,当前拥有的分数翻倍,而不是得到当前题号X2的分数。
代码:
~~~
#include <iostream>
using namespace std;
int main()
{
int que[11]={0,0,0,0,0,0,0,0,0,0,0};
int i,j,sum;
// 最高位不为1则循环下去
while(que[0]!=1)
{
// 最低位+1
++que[10];
// 模拟二进制加法
for(j=10;j>0;--j)
if(que[j]==2)
{
++que[j-1];
que[j]=0;
}
// 初始sum为10,并算总分
sum=10;
for(j=1;j<11;++j)
if(que[j]==1) sum=sum+sum;
else sum=sum-j;
// 如果最后总分为100,则输出
if(sum==100)
{
for(j=1;j<11;++j)
cout<<que[j];
cout<<endl;
}
}
return 0;
}
~~~
⑤欧拉与鸡蛋
大数学家欧拉在集市上遇到了本村的两个农妇,每人跨着个空篮子。她们和欧拉打招呼说两人刚刚卖完了所有的鸡蛋。
欧拉随便问:“卖了多少鸡蛋呢?”
不料一个说:“我们两人自己卖自己的,一共卖了150个鸡蛋,虽然我们卖的鸡蛋有多有少,但刚好得了同样的钱数。你猜猜看!”
欧拉猜不出。
另一个补充道:“如果我按她那样的价格卖,可以得到32元;如果她按我的价格卖,可以得到24.5元”。
欧拉想了想,说出了正确答案。
我们不是数学家,懒得列出公式来分析。但计算机可以“暴力破解”,就是把所有可能情况都试验一遍,撞上为止!
请写出每人鸡蛋的数目(顺序不限),用逗号隔开。
一道解一元二次方程的题目,设一个人以a元价格卖了x个鸡蛋,另一个人以b元价格卖了y个鸡蛋。
据题目分析:
x+y=150
ax=by
bx=32
ay=24.5
就是解这个方程,用笔算的话。。我觉得听麻烦的,还是暴力快点。。。
还要注意一点就是,后面两个也可以是:
ax=32
by=24.5
所以,应该有2*n(n为答案个数)的答案。
因为没有确定谁卖了多少个。
代码:
~~~
#include <iostream>
using namespace std;
int main()
{
int x,y;
for(x=1;x<=150;++x)
{
y=150-x;
if(32*y*y==24.5*x*x)
cout<<x<<" "<<y<<endl;
if(32*x*x==24.5*y*y)
cout<<x<<" "<<y<<endl;
}
return 0;
}
~~~
蓝桥杯结果填空题,注意一下几点,手算快于机算,就用手算,但是如果用机算,手也不要闲着,多划拉几下。
不要注重代码的优化,因为它只要结果,代码再漂亮人家也看不见,这时间可以用在后面编程大题上。
多注意题目给的线索,一般每个线索都能用上。
蓝桥杯-代码填空之四
最后更新于:2022-04-01 14:53:29
身份证校验—神秘三位数—生日相同概率—四方定理—因数分解—组合数
①身份证校验
如果让你设计个程序,用什么变量保存身份证号码呢?长整数可以吗?不可以!
因为有人的身份证最后一位是"X"
实际上,除了最后一位的X,不会出现其它字母!
身份证号码18位 = 17位 + 校验码
校验码的计算过程:
例如:身份证前17位 = ABCDEFGHIJKLMNOPQ
A~Q 每位数字乘以权值求和(每位数字和它对应的“权”相乘后累加)
17位对应的权值分别是:
7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
求出的总和再对11求模
然后按下表映射:
余数 0 1 2 3 4 5 6 7 8 9 10
校验码: 1 0 X 9 8 7 6 5 4 3 2
~~~
char verifyCode(char* s)
{
static int weight[] = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
static char map[] = {'1','0','X','9','8','7','6','5','4','3','2'};
int sum = 0;
for(int i=0; i<17; i++)
{
sum += (______________) * weight[i]; // 填空
}
return map[____________]; // 填空
}
~~~
题目中已经清楚说明,要做什么了,分成两步,第一步求权值,求权值的总和,然后对11求模,
根据余数进行相应映射,唯一注意一点就是,进来的字符肯定不是整型,需要减去48(即‘0’).
答案:s[i] - '0' sum % 11
②神秘的三位数
有这样一个3位数,组成它的3个数字阶乘之和正好等于它本身。即:abc = a! + b! + c!
下面的程序用于搜索这样的3位数。
~~~
int JC[] = {1,1,2,6,24,120,720,5040,40320,362880};
int i;
for(i=100; i<1000; i++)
{
int sum = 0;
int x = i;
while(__________)
{
sum += JC[x%10];
x /= 10;
}
if(i==sum) printf("%d\n", i);
}
~~~
题意很清晰,解法也明了,用JC数组存0!~9!,判断符不符合条件,肯定要一位数一位数取出来,
那就像判断水仙花数一样,%10再/10的循环。
答案:x>0
③生日相同的概率
生活中人们往往靠直觉来进行粗略的判断,但有的时候直觉往往很不可靠。比如:如果你们班有30名同学,那么出现同一天生日的概率有多大呢?你可能不相信,这个概率高达70%左右。
以下的程序就是用计算机随机模拟,再统计结果。
~~~
#define N 30
......
int a[N];
srand( time( NULL ) );
int n = 0;
for(int k=0; k<10000; k++)
{
for(int i=0; i<N; i++)
a[i] = rand() % 365;
bool tag = false; // 假设没有相同
for(i=1; i<N; i++)
{
for(int j=0; j<i; j++)
{
if(a[i]==a[j])
{
tag = true;
break;
}
}
_____________________;
}
if(tag) n++;
}
printf("%f\n", 1.0 * n / 10000 * 100);
~~~
30个人,同一天生日概率高达70%,( ⊙o⊙ )哇!
言归正传,解题。这道题,其实我没怎么大看,就看到当tag为true时,n++,但是当tag为true时候,虽然break,里面有两层for,于是乎,恍然大悟鸟。。。有时候解题并非要全弄懂他的方法,比赛时间有限啊= 。=
答案: if(tag)break;
④四方定理
数论中有著名的四方定理:所有自然数至多只要用四个数的平方和就可以表示。
我们可以通过计算机验证其在有限范围的正确性。
对于大数,简单的循环嵌套是不适宜的。下面的代码给出了一种分解方案。
~~~
int f(int n, int a[], int idx)
{
if(______________) return 1; // 填空1
if(idx==4) return 0;
for(int i=(int)sqrt(n); i>=1; i--)
{
a[idx] = i;
if(_______________________) return 1; // 填空2
}
return 0;
}
int main(int argc, char* argv[])
{
for(;;)
{
int number;
printf("输入整数(1~10亿):");
scanf("%d",&number);
int a[] = {0,0,0,0};
int r = f(number, a, 0);
printf("%d: %d %d %d %d\n", r, a[0], a[1], a[2], a[3]);
}
return 0;
}
~~~
大体一看代码结构,就知道,得,又是一道递归题目,递归方法,也不难理解,就是,原来数减去一个数的平方,再接着找,找的范围,也不用我们想,题目给了。递归结束条件,出了Idx肯定是n了,当n为0,代表找到了,不多说了,return 1。递归结束条件定是定了,可是,还有一个return 1,怎么办啊?
其实不难,仔细一想就明白了,for循环在找i,找到以后,肯定要向下递归下去,那肯定是要执行f函数了,执行f函数会返回值的,一个return0,一个return1,什么时候return1呢?必然就是当它返回1时,不断return1,回到主函数。
答案:n==0 f(n-i*i, a, idx+1) == 1
⑤因数分解
因数分解是十分基本的数学运算,应用广泛。下面的程序对整数n(n>1)进行因数分解。
比如,n=60, 则输出:2 2 3 5。请补充缺失的部分。
~~~
void f(int n)
{
for(int i=2; i<n/2; i++)
{
____________________
{
printf("%d ", i);
n = n / i;
}
}
if(n>1) printf("%d\n", n);
}
~~~
因数分解,简单吧?刚学语言的时候,肯定做过这种题目,蓝桥杯出的代码填空,对于我们大部分做过的题目,一般就会让我们来优化一下或者简洁一下代码,不可能,填我们原来做的时候的代码。
这道题,显然也是,你会发现i是一直在++的,例子中也出现了,有可能有两个2的情况。怎么回事呢?
我以前做用的是两个for,然后,找到以后i--,使得i再次为原值,进行判断。
除了for循环就剩下while了,所以咯,答案一跃而出啊。
答案:while(n%i==0)
⑥组合数
从4个人中选2个人参加活动,一共有6种选法。
从n个人中选m个人参加活动,一共有多少种选法?下面的函数实现了这个功能。
~~~
// n 个元素中任取 m 个元素,有多少种取法
int f(int n, int m)
{
if(m>n) return 0;
if(m==0) _______________;
return f(n-1,m-1) + _____________;
}
~~~
就是排列组合的问题嘛,4个人选2个,就是C四二。
n个人选m个就是Cnm
当m==0? C30==C33==1咯,所以return 1
第二个空如果想不起来,你不妨看一下判断条件,出现m>n return 0,输入的内容不会有m>n的情况吧?
所以只能是,我们递归的时候出现啦。
答案: return 1 f(n-1,m)
蓝桥杯-历届试题之翻硬币
最后更新于:2022-04-01 14:53:27
## 翻硬币
问题描述
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:**oo***oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:
输入格式
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000
输出格式
一个整数,表示最小操作步数。
样例输入1
******
o**o**
样例输出1
5
样例输入2
*o**o***o***
*o***o**o***
样例输出2
1
这道题啊,一道简单的贪心题目,两个字符串比较,如果发现不同,将当前和之后的翻到另一面,再继续寻找。
~~~
#include <iostream>
#include <string>
using namespace std;
string str1,str2;
void fan(int n)
{
if(str1[n]=='*')
str1[n]='o';
else if(str1[n]=='o')
str1[n]='*';
if(str1[n+1]=='*')
str1[n+1]='o';
else if(str1[n+1]=='o')
str1[n+1]='*';
}
int main()
{
int i,len,sum;
while(cin>>str1>>str2)
{
sum=0;
len=str1.length();
for(i=0;i<len;++i)
if(str1[i]!=str2[i])
{
++sum;
fan(i);
}
cout<<sum<<endl;
}
return 0;
}
~~~
蓝桥杯-代码填空之精品
最后更新于:2022-04-01 14:53:25
方阵旋转—取球概率—巧开平方
①方阵旋转
对一个方阵转置,就是把原来的行号变列号,原来的列号变行号
例如,如下的方阵:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
转置后变为:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
但,如果是对该方阵顺时针旋转(不是转置),却是如下结果:
13 9 5 1
14 10 6 2
15 11 7 3
16 12 8 4
下面的代码实现的功能就是要把一个方阵顺时针旋转。
~~~
void rotate(int* x, int rank)
{
int* y = (int*)malloc(___________________); // 填空
for(int i=0; i<rank * rank; i++)
{
y[_________________________] = x[i]; // 填空
}
for(i=0; i<rank*rank; i++)
{
x[i] = y[i];
}
free(y);
}
int main(int argc, char* argv[])
{
int x[4][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
int rank = 4;
rotate(&x[0][0], rank);
for(int i=0; i<rank; i++)
{
for(int j=0; j<rank; j++)
{
printf("%4d", x[i][j]);
}
printf("\n");
}
return 0;
}
~~~
这道题就是矩阵转置,只不过是顺时针的,看一遍代码,第二个for循环将x[i]=y[i] 然后把y给free掉,y肯定是中间变量,用来存正确的放置方法的,所以它的大小应该等同于X的大小。
那么第一个空肯定就是rank*rank呗,如果这样写提交,肯定一个大红叉叉在上面。Why?因为你开辟的空间,确定是
rank*rank吗?你心里把它默认为Int形式,可是代码不知道啊!所以还需要再乘一个4,作为int型的大小。如果更保险一些,你可以写成 sizeof(int)。
这个如果自己编一个程序没有乘4或者sizeof(int),程序会给你报错的。(PS:malloc 需要头文件 malloc.h)
然后第二个空,开始我还纳闷他是怎么用一个i来进行二维数组的赋值?后来经好友提醒,顿时恍悟:
二维数组可以写成这样:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
5可以用 1,0,同样也可以用长度 1*4+1啊。
所以懂了吧?就是一行的宽度有限个,当你输入的宽度大于该行的宽度,它会给你换下一行,继续找。
答案:
sizeof(int) * rank * rank 或者 4 * rank * rank
(i%rank)*rank+(rank-(i/rank)-1)
②取球概率
口袋中有5只红球,4只白球。随机从口袋中取出3个球,则取出1个红球2个白球的概率是多大?
类似这样的数学问题,在计算的时候往往十分复杂。但如果通过计算机模拟这个过程,比如进行100000次取球模拟,统计一下指定情况出现的次数对计算机来说是方便且快速的。
~~~
srand( (unsigned)time( NULL ) );
int n = 0;
for(int i=0; i<100000; i++)
{
char x[] = {1, 1, 1, 1, 1, 2, 2, 2, 2};
int a = 0; // 取到的红球的数目
int b = 0; // 取到的白球的数目
for(int j=0; j<3; j++)
{
int k = rand() % (9-j);
if(x[k]==1)
a++;
else
b++;
_______________________;
}
if(a==1 && b==2) n++;
}
printf("概率=%f\n", n/100000.0*100);
~~~
题目很简单就是模拟取球,空格上填的第一遍看也能懂,就是怕取道的那个下标的球去掉,但怎么去除?
赋0?赋0的话下次碰到会以else 让b++,显然不可以。
其实,题目中已经给了你隐藏的信息,题中每次rand()值对9-j取模而不是9,也就是说,第一次对9取余,
第二次对8取余,第三次对7取余,这样第一次所有球都是好球,第二次,最后一个球是费球,用不到,
第三次最后两个都是废球,也用不到。如此一来,我们完全可以把取到的球和最后的互换,这样取到的球,
可以扔到后面做废球,而后面的可以换到前面,继续来随机取球。
答案: x[k] = x[9-j-1]
③开平方
开平方
如果没有计算器,我们如何求2的平方根?
可以先猜测一个数,比如1.5,然后用2除以这个数字。如果我们猜对了,则除法的结果必然与我们猜测的数字相同。我们猜测的越准确,除法的结果与猜测的数字就越接近。
根据这个原理,只要我们每次取猜测数和试除反馈数的中间值作为新的猜测数,肯定更接近答案!这种计算方法叫做“迭代法”。
下面的代码模拟了如何用手工的方法求2的平方根的过程。
~~~
double n = 2;
double a = 0;
double b = n;
while(fabs(a-b)>1E-15)
{
a = (a+b)/2;
b = __________;
}
printf("%f\n", a);
~~~
很短的代码,就是用迭代法求平方根,题目中给出了: 只要我们每次取猜测数和试除反馈数的中间值 作为新的猜测数,肯定接近答案。根据代码,我们也明白,a肯定是猜测数,b肯定是试除反馈数。(为什么?因为a=(a+b)/2,
a是新的猜测数,新猜测数的产生,根据题意就明白了b肯定是试除反馈数),刚开始读题,我始终搞不懂什么叫试
除反馈数,b要怎么求呢?
再仔细看看题目中的描述,b是 试除反馈数,反馈数不就是我们求的猜测数吗,于是乎,答案随之跃出。
答案:n/a
蓝桥杯-代码填空之三
最后更新于:2022-04-01 14:53:22
连续和的平方数—排列的个数—巧放棋子—取中间数字—去掉重复字母—日历天数之差
①连续和的平方数
1+3 = 4, 1+3+5 = 9, 1+3+5+7 = 16 它们的结果都是平方数。
这是偶然的巧合吗?下面代码验证对于累加至1000以内的情况都成立。
~~~
int n = 1;
for(int i=1; i<1000/2; i++)
{
n += 2 * i + 1;
int m = ______________;
if( m * m != n)
{
printf("加至%d 时不成立!\n", 2 * i + 1);
break;
}
}
~~~
连续和的平方数,我刚开始还在那找规律,发现 n最后加上的数字再加1 再除以2 所得到的数的平方就是最后那个平方数, 化简下来: (2*i+1+1)/2=i+1 然后突然发现我二了= 。=
答案:i+1
②排列的个数
计算3个A,2个B可以组成多少种排列的问题(如:AAABB, AABBA)是《组合数学》的研究领域。
但有些情况下,也可以利用计算机计算速度快的特点通过巧妙的推理来解决问题。
下列的程序计算了m个A,n个B可以组合成多少个不同排列的问题。
~~~
int f(int m, int n)
{
if(m==0 || n==0) return 1;
return _______________________;
}
~~~
这道题,一看就知道递归题目了,做了这么多代码填空题我也发现了,看见只给一个函数,最上面还有一些判断,return的,一般跑不了递归。在本上画一画,算一算,不难求出来。
答案: f(m-1,n)+f(m,n-1)
③巧放棋子
今有 6 x 6 的棋盘格。其中某些格子已经预先放好了棋子。
现在要再放上去一些,使得:每行每列都正好有3颗棋子。
我们希望推算出所有可能的放法。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-22_576a44af9afbe.jpg)
初始数组中,“1”表示放有棋子,“0”表示空白。
~~~
int N = 0;
bool CheckStoneNum(int x[][6])
{
for(int k=0; k<6; k++)
{
int NumRow = 0;
int NumCol = 0;
for(int i=0; i<6; i++)
{
if(x[k][i]) NumRow++;
if(x[i][k]) NumCol++;
}
if(_____________________) return false; // 填空
}
return true;
}
int GetRowStoneNum(int x[][6], int r)
{
int sum = 0;
for(int i=0; i<6; i++) if(x[r][i]) sum++;
return sum;
}
int GetColStoneNum(int x[][6], int c)
{
int sum = 0;
for(int i=0; i<6; i++) if(x[i][c]) sum++;
return sum;
}
void show(int x[][6])
{
for(int i=0; i<6; i++)
{
for(int j=0; j<6; j++) printf("%2d", x[i][j]);
printf("\n");
}
printf("\n");
}
void f(int x[][6], int r, int c);
void GoNext(int x[][6], int r, int c)
{
if(c<6)
_______________________; // 填空
else
f(x, r+1, 0);
}
void f(int x[][6], int r, int c)
{
if(r==6)
{
if(CheckStoneNum(x))
{
N++;
show(x);
}
return;
}
if(______________) // 已经放有了棋子 // 填空
{
GoNext(x,r,c);
return;
}
int rr = GetRowStoneNum(x,r);
int cc = GetColStoneNum(x,c);
if(cc>=3) // 本列已满
GoNext(x,r,c);
else if(rr>=3) // 本行已满
f(x, r+1, 0);
else
{
x[r][c] = 1;
GoNext(x,r,c);
x[r][c] = 0;
if(!(3-rr >= 6-c || 3-cc >= 6-r)) // 本行或本列严重缺子,则本格不能空着!
GoNext(x,r,c);
}
}
int main(int argc, char* argv[])
{
int x[6][6] = {
{1,0,0,0,0,0},
{0,0,1,0,1,0},
{0,0,1,1,0,1},
{0,1,0,0,1,0},
{0,0,0,1,0,0},
{1,0,1,0,0,1}
};
f(x, 0, 0);
printf("%d\n", N);
return 0;
}
~~~
这道题代码不短,莫慌莫急,慢慢缕一缕就很好解决了,
先看最下面的空,后面还有注释: 本格有棋子,那还用想填什么吗?当然是当数组内数为1的时候啊。
再看倒数第二个,下面那个else是r+1,然后c归0,显然是换行操作,什么时候需要换行?肯定读到末尾了,
就是c>6的时候。
再看第一个,乍一看,真不懂,回程序找找哪里出现这个函数调用,发现调用这个函数,返回1的时候N自增,
就是棋子的数目增加一个了,那就简单了,肯定是判断该行,该列棋子是否都为3了。如果符合这个条件,就
可以放棋子,棋子总数++。
答案:
~~~
NumRow!=3 || NumCol!=3
f(x, r, c+1)
x[r][c] 或 x[r][c]==1
~~~
④取中间数字
假设a,b,c是3个互不相等的整数。下列代码取出它们中居中的数值,记录在m中。
其中的swap()函数可以交换两个变量的值。
~~~
if(a>b) swap(&a, &b);
if(b>c) swap(&b, &c);
______________________;
int m = b;
~~~
这题目,很容易理解吧,互不相等的整数,取中间那个,肯定三个数需要两两比较,
第一个if进行了a,b 比较,第二个if 进行 b,c比较,
第三个肯定也需要比较一次,是比较a,b呢还是b,c呢?
可以这样看:
如果a<b, 第一个If没有执行,b与c比较,b是b,c中小的那个,那么肯定需要再与a比较一次,要去中间的嘛。
如果a>b,第一个if执行,再将b,c比较,b依旧是较小的那个,b,c中较小的再与原来的b即现在的a比较,取中间。
上面的话,是不是很绕啊。。。
就是因为b,c比较在后,肯定无法与a,b中较大的数比较,所以要增设一个a,b比较。
实在不理解可以列几组数据来算一下。
答案: if(a>b) swap(&a,&b)
⑤去掉重复字母
给定一个串,例如“aabbbcddddkkkmmmmaakkkk”我们希望去掉连续的重复字母,
得出串:“abcdkmak”,下面代码实现了该功能,请完善之。
~~~
char* p = "aabbbcddddkkkmmmmaakkkk";
char buf[100];
char* q = p;
int i=0;
for(;*q;)
{
if(___________|| *q != *(q-1))
{
buf[i++] = *q;
}
q++;
}
buf[i] = '\0';
printf("%s\n", buf);
~~~
任务明确,代码完整, 将不重复的字符存到buf数组中,q指向p头部即a的位置, for循环第一第三条件空缺,判断q是否等于NULL, 什么情况下 会将指针q的内容赋值给buf呢?if已经写一半了,就是当当前指针指向的内容不等于之前指针指向的内容的时候,会赋值,这就有一个问题了,如果指针指向第一个位置,怎么办?所以空缺的地方显而易见了。
答案:p==q
⑥日历天数差
人类历史上出现了很多种历法。现行的公历即格里历由儒略历改革而来。它是目前较为精确和规则简明的一种历法,约3300年误差一日。因为闰年问题以及每个月的长度不等,仍然使得某些计算较为麻烦。
比如:求两个日期间差多少天。
下面的代码实现了求两个由公历表示的日期间差多少天的功能。
其计算原理是先求出每个日期距离1年1月1日的天数差值,再进一步做差即可。
请研读代码,填写缺失的部分。
~~~
struct MyDate
{
int year;
int month;
int day;
};
int GetAbsDays(MyDate x)
{
int i;
int month_day[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int year = x.year-1; // 因为欲求距离1年1月1日的距离
int days = year * 365 + year/4 - year/100 + year/400;
if(x.year%4==0 && x.year%100!=0 || x.year%400==0) month_day[1]++;
for(i=0; i<______________; i++)
days += month_day[i];
days += x.day-1;
return days;
}
int GetDiffDays(MyDate a, MyDate b)
{
return GetAbsDays(b) - GetAbsDays(a);
}
int main(int argc, char* argv[])
{
MyDate a = {1842,5,18};
MyDate b = {2000,3,13};
int n = GetDiffDays(a,b);
printf("%d\n", n);
}
~~~
这道题无外乎求,某个日期到1年1月1日的时间,一看到计算年月日的,我就直接看空缺处了,for循环体内是
days+=month_day[i],就是总天数加上该月份拥有的天数,那还用想吗?肯定填当前月份了,
但是假设日期是 2014年3月22日,月份肯定不能等于3,所以就是x.month-1咯
答案:x.month-1
蓝桥杯-代码填空之二
最后更新于:2022-04-01 14:53:20
干支纪年法—歌赛新规则—红球多于白球的概率—交换变量—考拉兹猜想—利息计算
①干支纪年法
在我国古代和近代,一直采用干支法纪年。它采用10天干和12地支配合,一个循环周期为60年。
10天干是:甲,乙,丙,丁,戊,己,庚,辛,壬,癸
12地支是:子,丑,寅,卯,辰,巳,午,未,申,酉,戌,亥
如果某年是甲子,下一年就是乙丑,再下是丙寅,......癸酉,甲戌,乙亥,丙子,....
总之天干、地址都是循环使用,两两配对。
今年(2012)是壬辰年,1911年辛亥革命
下面的代码根据公历年份输出相应的干支法纪年。已知最近的甲子年是1984年。
~~~
void f(int year)
{
char* x[] = {"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"};
char* y[] = {"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"};
int n = year - 1984;
while(n<0) n += 60;
printf("%s%s\n", x[_______], y[_______]);
}
int main(int argc, char* argv[])
{
f(1911);
f(1970);
f(2012);
return 0;
}
~~~
这道题,最近的一个甲午年(就是对10或者12取模都为0)是1984年,就以它为标准,求模就可以了,
题目中也有对给出的年份小于1984年的处理(n+=60),这题难度,应该很小了。。。
答案: n%10 n%12
②歌赛新规
歌手大赛的评分规则一般是去掉一个最高分,去掉一个最低分,剩下的分数求平均。当评委较少的时候,如果我们只允许去掉一个分数,该如何设计规则呢?
有人提出:应该去掉与其余的分数平均值相差最远的那个分数。即“最离群”的分数。
以下的程序用于实现这个功能。其中x存放所有评分,n表示数组中元素的个数。函数返回最“离群”的那个分数值。
~~~
double score(double x[], int n)
{
int i,j;
double dif = -1;
double bad;
for(i=0; i<n; i++)
{
double sum = 0;
for(j=0; j<n; j++)
{
if(________) sum += x[j];
}
double t = x[i] - sum / (n-1);
if(t<0) t = -t;
if(t>dif)
{
dif = t;
bad = x[i];
printf("%d, %f\n", i, x[i]);
}
}
return bad;
}
~~~
题目很简单,就是求最离群的数字,如果让我打代码,我猜可能是求最大和最小的,然后剩下的求平均,通过它们之间的差值来查找,这题目的做法,应该是,计算n-1个平均值,来比较,所以两层循环,
第一层,计算2~n的,第二层计算1,3~n。。。。所以if里应该是去除掉当前循环的i,对应的值再求和
答案:i!=j
③概率问题
某个袋子中有红球m个,白球n个。现在要从中取出x个球。那么红球数目多于白球的概率是多少呢?
下面的代码解决了这个问题。其中的y表示红球至少出现的次数。
这与前文的问题是等价的。因为如果取30个球,要求红球数大于白球数,则等价于至少取出16个红球。
~~~
/*
m: 袋中红球的数目
n: 袋中白球的数目
x: 需要取出的数目
y: 红球至少出现的次数
*/
double pro(int m, int n, int x, int y)
{
if(y>x) return 0;
if(y==0) return 1;
if(y>m) return 0;
if(x-n>y) return 1;
double p1 = _______________________;
double p2 = _______________________;
return (double)m/(m+n) * p1 + (double)n/(m+n) * p2;
}
~~~
刚开始,我以为要直接求出来p1,p2,但是后来一想,不对啊,代码填空题,只给了一个函数,没有给主函数那些,肯定是递归了,再加上题目中给了递归终止的条件,所以肯定是递归了。
知道了递归以后就很简单了:模拟拿球情况,要么拿了一个红球,要么拿了一个白球。
答案:pro(m-1,n,x-1,y-1) pro(m,n-1,x-1,y)
④交换变量
如果要把两个整型变量a、b的值交换,一般要采用一个中间变量做过渡,
但也可以在不借助任何其它变量的情况下完成。
a = _________;
b = _________;
a = _________;
这道题目,有很多种解法,我这里就给出两种吧,一个是用位运算— ^
^(异或)是将两边数都转换成2进制,然后异或,
第一种方法:a=a^b,b=a^b,a=a^b
第二种方法就是 a=a+b,b=a-b,a=a-b
⑤考拉兹猜想
“考拉兹猜想”(又称3n+1猜想、角谷猜想、哈塞猜想、乌拉姆猜想或叙拉古猜想) 和“哥德巴赫猜想”一样目前还没有用数学方法证明其完全成立。在1930年,德国汉堡大学的学生考拉兹,曾经研究过这个猜想,因而得名。在1960年,日本人角谷静夫也研究过这个猜想。
该猜想的叙述十分简单:从任何一个正整数n出发,若是偶数就除以2,若是奇数就乘3再加1,如此继续下去,经过有限步骤,总能得到1。例如:
17-52-26-13-40-20-10-5-16-8-4-2-1
该猜想虽然没有完全证明,但用计算机验证有限范围的数字却十分容易。
~~~
for(int n=2; n<=10000; n++)
{
int m = n;
for(;;)
{
if(____________)
m = m / 2;
else
m = m * 3 + 1;
if( m == 1 )
{
printf("%d ok! \n", n);
break;
}
}
};
~~~
这道题,额,看起来很高端大气上档次,猜想也很厉害的样子,但是空就有些。。。
根据题目所给,遇到偶数时 该数除以2,所以答案就是判断m是不是偶数: m%2==0
⑥利息计算
小李年初在银行存款1千元(一年定期)。他计划每年年底取出100元救助失学儿童。
假设银行的存款利率不变,年利率为3%,年底利息自动计入本金。下面的代码计算5年后,该账户上有多少存款。
~~~
double money = 1000;
int n = 5;
int i;
for(i=0; i<n; i++)
{
money = _______________;
money -= 100;
}
printf("%.2f\n", money);
~~~
这道题啊,唉,每年年末,要把本金加上利息都算上再存进去,扣的钱就不需要减了,下面代码帮助你减了。
答案:money=money*1.03
蓝桥杯-代码填空之一
最后更新于:2022-04-01 14:53:18
N进制小数—车票找零方案计数—串的反转—串的轮换—大数分块乘法—二进制串转整数
①N进制小数
将任意十进制正小数分别转换成2,3,4,5,6,7,8,9进制正小数,小数点后保留8位,并输出。例如:若十进制小数为0.795,则输出:
十进制正小数 0.795000 转换成 2 进制数为: 0.11001011
十进制正小数 0.795000 转换成 3 进制数为: 0.21011011
十进制正小数 0.795000 转换成 4 进制数为: 0.30232011
十进制正小数 0.795000 转换成 5 进制数为: 0.34414141
十进制正小数 0.795000 转换成 6 进制数为: 0.44341530
十进制正小数 0.795000 转换成 7 进制数为: 0.53645364
十进制正小数 0.795000 转换成 8 进制数为: 0.62702436
十进制正小数 0.795000 转换成 9 进制数为: 0.71348853
以下代码提供了这个功能。其中,dTestNo表示待转的十进制小数。iBase表示进制数。请填写缺失的部分。
~~~
void fun(double dTestNo, int iBase)
{
int iT[8];
int iNo;
printf("十进制正小数 %f 转换成 %d 进制数为: ",dTestNo, iBase);
for(iNo=0;iNo<8;iNo++)
{
dTestNo *= iBase;
iT[iNo] = ________________;
if(___________________) dTestNo -= iT[iNo];
}
printf("0.");
for(iNo=0; iNo<8; iNo++) printf("%d", iT[iNo]);
printf("\n");
}
void main ( )
{
double dTestNo= 0.795;
int iBase;
for(iBase=2;iBase<=9;iBase++)
fun(dTestNo,iBase);
printf("\n");
}
~~~
这道题看到函数传进来的是一个小数和进制数,
题中建立一个大小为8的数组,再看看输出,就知道,是把每一个相应进制数存在数组中,
在本上算一算就发现,小数乘以相应进制,整数部分即为该进制第一个小数(这个是小数转换进制的基础知识),
显然题目,也是这么做的,填空就不难了,第一个就是取整数部分,直接强制类型转换就可以,
第二个就是判断什么时候需要减去整数部分,前面是*=,原来的小数变化了,求下一个数,肯定要把整数部分清零,
所以就是,当数组内容不为0的时候,也可以说是大于0的时候。
答案:(答案不唯一,只要能满足要求即可)
空1: (int)dTestNo
空2: dTestNo>=1.0
②车票找零方案计数
公交车票价为5角。假设每位乘客只持有两种币值的货币:5角、1元。
再假设持有5角的乘客有m人,持有1元的乘客有n人。由于特殊情况,开始的时候,售票员没有零钱可找。
我们想知道这m+n名乘客以什么样的顺序购票则可以顺利完成购票过程。
显然,m < n的时候,无论如何都不能完成,m >=n的时候,有些情况也不行。
比如,第一个购票的乘客就持有1元。
下面的程序计算出这m+n名乘客所有可能顺利完成购票的不同情况的组合数目。
注意:只关心5角和1元交替出现的次序的不同排列,持有同样币值的两名乘客交换位置并不算做一种新的情况来计数。
//m: 持有5角币的人数
//n: 持有1元币的人数
//返回:所有顺利完成购票过程的购票次序的种类数
~~~
int f(int m, int n)
{
if(m < n) return 0;
if(n==0) return 1;
return _______________________;
}
~~~
这道题,一看函数类型int 还有终止条件,显然是递归,
递归的话就简单了,你是 return f(m-1,n-1)+1还是 return f(m-1,n)+f(m,n-1) 呢?
m-1,n-1就是 当前人数,先来一个五毛钱的,再来一个1元的, 那排序基本上就是 0.5 1 0.5 1显然方案不全,
所以就是m-1,n + m,n-1了,
答案:
f(m-1, n) + f(m, n-1)
③反转串
我们把“cba”称为“abc”的反转串。
下面的代码可以把buf中的字符反转。其中n表示buf中待反转的串的长度。请补充缺少的代码。
~~~
void reverse_str(char* buf, int n)
{
if(n<2) return;
char tmp = buf[0];
buf[0] = buf[n-1];
buf[n-1] = tmp;
_______________________________;
}
~~~
题目要求很简单,就是把串反转,看题干,也很明白它的做法,
就是取头尾,互换,下一步肯定是往里缩进一格,首位都缩进,肯定长度减少2了,
这样,接着把串和长度传下去就可以了,也是一个递归,因为传递的是指针,所以不需要返回值。
答案:
reverse_str(buf+1,n-2)
(答案不唯一)
④串的轮换
串“abcd”每个字符都向右移位,最右的移动到第一个字符的位置,就变为“dabc”。这称为对串进行位移=1的轮换。同理,“abcd”变为:“cdab”则称为位移=2的轮换。
下面的代码实现了对串s进行位移为n的轮换。请补全缺失的代码。
~~~
void shift(char* s, int n)
{
char* p;
char* q;
int len = strlen(s);
if(len==0) return;
if(n<=0 || n>=len) return;
char* s2 = (char*)malloc(_________);
p = s;
q = s2 + n % len;
while(*p)
{
*q++ = *p++;
if(q-s2>=len)
{
*q = ___________;
q = s2;
}
}
strcpy(s,s2);
free(s2);
}
~~~
这道题,我是从下往上看的,看到最后有个 strcpy(s,s2) 和 free(s2) 就明白,
它的方法就是建立一个中间串s2,s2存储正确顺序,最后直接赋值给s,
过程,malloc肯定就是开辟s2的空间,开辟空间大小显然与s长度len有关,
后面p指针指向s头部,q指向中间串s2的相应位置,n%len 就是计算具体指向那个地方,
然后通过循环,当p!=NULL时,将p指向内容赋值给q指向内容,然后两者再往后移动,
这里要注意是先赋值再移动,
假如题目是 abcd 2,那么通过上述循环,s2串内容将是 空空ab (空代表什么都没有)
那个if就是判断q是否指到了末尾,当指到末尾,就给q赋值为NULL,
将q指向s2头部,接着赋值。这样就达到了构建s2的目的。
简单的说,就是:
p是从s头指向尾不变,
而q从s2中间位置向后移动,如果长度大于等于串长度,再指向s2头部,
以 abcd 2为例就是:
p从指向a移动到d,
q先指向c的位置,将a,b,赋值到s2串第3,4的位置,if成立,所以将后面第5个设置NULL,
指回s2头部,这是p指向的是c,到指到d下一个是空,所以循环跳出,s2串构建完成。
答案:
len+1
0(指的是空,也可以写成NULL或者‘\0'或者(char)0
⑤大数分块乘法
对于32位字长的机器,大约超过20亿,用int类型就无法表示了,我们可以选择int64类型,
但无论怎样扩展,固定的整数类型总是有表达的极限!如果对超级大整数进行精确运算呢?
一个简单的办法是:仅仅使用现有类型,但是把大整数的运算化解为若干小整数的运算,即所谓:“分块法”。
如图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-22_576a44af781f3.jpg)
表示了分块乘法的原理。可以把大数分成多段(此处为2段)小数,然后用小数的多次运算组合表示一个大数。
可以根据int的承载能力规定小块的大小,比如要把int分成2段,则小块可取10000为上限值。
注意,小块在进行纵向累加后,需要进行进位校正。
以下代码示意了分块乘法的原理(乘数、被乘数都分为2段)。
~~~
void bigmul(int x, int y, int r[])
{
int base = 10000;
int x2 = x / base;
int x1 = x % base;
int y2 = y / base;
int y1 = y % base;
int n1 = x1 * y1;
int n2 = x1 * y2;
int n3 = x2 * y1;
int n4 = x2 * y2;
r[3] = n1 % base;
r[2] = n1 / base + n2 % base + n3 % base;
r[1] = ____________________________________________; // 填空
r[0] = n4 / base;
r[1] += _______________________; // 填空
r[2] = r[2] % base;
r[0] += r[1] / base;
r[1] = r[1] % base;
}
int main(int argc, char* argv[])
{
int x[] = {0,0,0,0};
bigmul(87654321, 12345678, x);
printf("%d%d%d%d\n", x[0],x[1],x[2],x[3]);
return 0;
}
~~~
这题应该比较简单了,根据图很容易填上第一个空,
要注意它的r[3]、r[2]、r[1]、r[0]对应图中r4,r3,r2,r1,
第二个空,根据下面r0也可以照葫芦画瓢的填出来。
答案:(答案不唯一)
n2 / base + n3 / base + n4 % base
r[2] / base
⑥二进制串转整数
下列代码把一个二进制的串转换为整数。请填写缺少的语句;
~~~
char* p = "1010110001100";
int n = 0;
for(int i=0;i<strlen(p); i++)
{
n = __________________;
}
printf("%d\n", n);
~~~
就是将2进制的串转换成10进制,
如果有cmath库函数,直接用个Pow,
这题好像没提供,
但是,不要忘了,将10进制转换2进制可以用while循环做:
~~~
i=0
while(num>2)
{
arr[i++]=num%2;
num/=2;
}
~~~
这题完全可以逆着来。
还要注意一下,题目中的是字符,而不是数字。
答案:
n * 2 + (p[i] - '0')
上述题目的答案都不是唯一的,当答案和标准答案不一样时,会将答案带入程序运行,通过运行结果来判断正误。
算法训练之最大最小公倍数——ALGO-2
最后更新于:2022-04-01 14:53:15
## 算法训练 最大最小公倍数
时间限制:1.0s 内存限制:256.0MB
问题描述
已知一个正整数N,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。
输入格式
输入一个正整数N。
输出格式
输出一个整数,表示你找到的最小公倍数。
样例输入
9
样例输出
504
数据规模与约定
1 <= N <= 106。
这道题,首先说明一下,公认的5,8,9,10四个测试数据有错误,详情看论坛。。。
还有,类型用long long,
题意很简单,就是求三个互质的数,何为互质?两两最大公约数为1,
而且,在所有两两互质的数中,肯定值越大乘积越大,因此,可以向这方面做。
但是,列出几个例子就会发现,这题目是有规律的:
例1: 1 2 3 4 5 6 (n为6)
例2: 1 2 3 4 5 6 7(n为7)
例3: 1 2 3 4 5 6 7 8 (n为8)
规律:
①. 当n为奇数时, n*n-1*n-2最大
②. 当n为偶数时,有两个选择方案,要么选n-1*n-2*n-3 要么选n*n-1*n-3,这两个选择差就差在与3的余数关系,
这个优先策略很简单,尽量先选最大的,然后往下选,但是,如果你选了最大的以后,比如例1中,选了6,就
无法选择4也无法选择3,甚至2,所以,这种情况就不能去选择6,只能退一步选择n-1,再讨论
这样代码就出来了:
~~~
#include<iostream>
using namespace std;
int main()
{
long long n, number;
cin>>n;
if( n <= 2)
{
cout<<2;
}
else if(n % 2)
{
number = n * (n - 1) * (n - 2);
cout<<number;
}
else
{
if( n % 3 == 0)
{
number = (n - 1) * (n - 2) * (n - 3) ;
}
else number = n * (n - 1) * (n - 3);
cout<<number;
}
return 0;
}
~~~
算法训练之区间K大数查询——ALGO-1
最后更新于:2022-04-01 14:53:13
## 算法训练 区间k大数查询
时间限制:1.0s 内存限制:256.0MB
问题描述
给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。
输入格式
第一行包含一个数n,表示序列长度。
第二行包含n个正整数,表示给定的序列。
第三个包含一个正整数m,表示询问个数。
接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。
输出格式
总共输出m行,每行一个数,表示询问的答案。
样例输入
5
1 2 3 4 5
2
1 5 2
2 3 2
样例输出
4
2
数据规模与约定
对于30%的数据,n,m<=100;
对于100%的数据,n,m<=1000;
保证k<=(r-l+1),序列中的数<=106。
~~~
// k 大数查询
#include <iostream>
#include <algorithm>
using namespace std;
int arr[1001],brr[1001];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,m;
int l,r,k;
int i,j;
while(cin>>n)
{
for(i=0;i<n;++i)
cin>>arr[i];
cin>>m;
while(m--)
{
cin>>l>>r>>k;
// 将相应的区间复制到另一个数组中,排序
for(j=l-1,i=0;j<r;++j,++i)
brr[i]=arr[j];
sort(brr,brr+i,cmp);
cout<<brr[k-1]<<endl;
}
}
return 0;
}
~~~
基础练习之数列排序——BASIC-13
最后更新于:2022-04-01 14:53:11
## 基础练习 数列排序
时间限制:1.0s 内存限制:512.0MB
问题描述
给定一个长度为n的数列,将这个数列按从小到大的顺序排列。1<=n<=200
输入格式
第一行为一个整数n。
第二行包含n个整数,为待排序的数,每个整数的绝对值小于10000。
输出格式
输出一行,按从小到大的顺序输出排序后的数列。
样例输入
5
8 3 6 4 9
样例输出
3 4 6 8 9
~~~
// 数列排序
#include <iostream>
#include <algorithm>
using namespace std;
int arr[100001];
int main()
{
int n,i;
while(cin>>n)
{
for(i=0;i<n;++i)
cin>>arr[i];
sort(arr,arr+n);
for(i=0;i<n;++i)
{
if(i==n-1)
cout<<arr[i]<<endl;
else
cout<<arr[i]<<" ";
}
}
return 0;
}
~~~
基础练习之十六进制转八进制——BASIC-12
最后更新于:2022-04-01 14:53:09
## 基础练习 十六进制转八进制
时间限制:1.0s 内存限制:512.0MB
问题描述
给定n个十六进制正整数,输出它们对应的八进制数。
输入格式
输入的第一行为一个正整数n (1<=n<=10)。
接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。
输出格式
输出n行,每行为输入对应的八进制正整数。
注意
输入的十六进制数不会有前导0,比如012A。
输出的八进制数也不能有前导0。
样例输入
2
39
123ABC
样例输出
71
4435274
提示
先将十六进制数转换成某进制数,再由某进制数转换成八进制。
刚开始,犯得老毛病,就是先转换成了十进制,忘了题目中有一句话,每个十六进制数长度不超过100000,十万啊,大数问题了,用字符串吧。
这是错误的,我转换十进制再转换八进制的代码:
~~~
// 十六进制转八进制,WA
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int arr[10000001]; // 记录八进制的数
int main()
{
string str;
int n,m,i,num,j;
cin>>n;
while(n--)
{
cin>>str;
m=str.length();
// 转换成十进制
num=0;
for(i=m-1;i>=0;--i)
{
if(str[i]>='0'&&str[i]<='9')
num+=pow(16,m-1-i)*(str[i]-'0');
else if(str[i]>='A'&&str[i]<='F')
num+=pow(16,m-1-i)*(str[i]-'A'+10);
}
i=0;
while(num/8!=0)
{
arr[i]=num%8;
num/=8;
++i;
}
arr[i]=num;
for(j=i;j>=0;--j)
cout<<arr[j];
cout<<endl;
}
return 0;
}
~~~
这个是正确的,先转换二进制再转换八进制:
// 修正位数,那里,因为转换成八进制,要将二进制三个一组分组,如果不能被三整除,会出错,所以
要修正位数,往前面填0,填0后要防止转换八进制后第一个为0,输出时判断一下。
// 十六进制转换8进制 AC
~~~
#include <iostream>
#include <string>
using namespace std;
int arr[10000001];
int main()
{
int n,len_str,i,j;
string str,str2;
cin>>n;
while(n--)
{
cin>>str;
len_str=str.length();
str2="";
// 十六进制转换为二进制
for(i=0;i<len_str;++i)
{
switch(str[i])
{
case '0':str2+="0000";break;
case '1':str2+="0001";break;
case '2':str2+="0010";break;
case '3':str2+="0011";break;
case '4':str2+="0100";break;
case '5':str2+="0101";break;
case '6':str2+="0110";break;
case '7':str2+="0111";break;
case '8':str2+="1000";break;
case '9':str2+="1001";break;
case 'A':str2+="1010";break;
case 'B':str2+="1011";break;
case 'C':str2+="1100";break;
case 'D':str2+="1101";break;
case 'E':str2+="1110";break;
case 'F':str2+="1111";break;
default:break;
}
}
~~~
// 修正位数,这里用十六进制的长度来对3取余,其实用2进制的
//长度也是一样的,因为1个16进制对应4个二进制,所以取余效果是相同的
~~~
if(len_str%3==1)str2="00"+str2;
else if(len_str%3==2)str2="0"+str2;
len_str=str2.length();
// 二进制转换八进制
j=0;
for(i=0;i<=len_str-2;i+=3)
{
arr[j]=(str2[i]-'0')*4+(str2[i+1]-'0')*2+(str2[i+2]-'0');
++j;
}
for(i=0;i<j;++i)
{
if(i==0 && arr[i]==0)continue;
cout<<arr[i];
}
cout<<endl;
}
return 0;
}
~~~
基础练习之十六进制转十进制——BASIC-11
最后更新于:2022-04-01 14:53:06
## 基础练习 十六进制转十进制
时间限制:1.0s 内存限制:512.0MB
问题描述
从键盘输入一个不超过8位的正的十六进制数字符串,将它转换为正的十进制数后输出。
注:十六进制数中的10~15分别用大写的英文字母A、B、C、D、E、F表示。
样例输入
FFFF
样例输出
65535
~~~
// 十六进制转换十进制
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
long long num;
int main()
{
string str;
int n,m,i;
while(cin>>str)
{
m=str.length();
// 转换成十进制
num=0;
for(i=m-1;i>=0;--i)
{
if(str[i]>='0'&&str[i]<='9')
num+=pow(16,m-1-i)*(str[i]-'0');
else if(str[i]>='A'&&str[i]<='F')
num+=pow(16,m-1-i)*(str[i]-'A'+10);
}
cout<<num<<endl;
}
return 0;
}
~~~
基础练习之十进制转十六进制——BASIC-10
最后更新于:2022-04-01 14:53:03
## 基础练习 十进制转十六进制
时间限制:1.0s 内存限制:512.0MB
问题描述
十六进制数是在程序设计时经常要使用到的一种整数的表示方式。它有0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F共16个符号,分别表示十进制数的0至15。十六进制的计数方法是满16进1,所以十进制数16在十六进制中是10,而十进制的17在十六进制中是11,以此类推,十进制的30在十六进制中是1E。
给出一个非负整数,将它表示成十六进制的形式。
输入格式
输入包含一个非负整数a,表示要转换的数。0<=a<=2147483647
输出格式
输出这个整数的16进制表示
样例输入
30
样例输出
1E
~~~
// 十进制转换十六进制
#include <iostream>
using namespace std;
long long num,k;
char arr[10000001];
int main()
{
int i;
while(cin>>num)
{
i=0;
while(num/16>0)
{
k=num%16;
if( k>=0 && k<=9 )
arr[i]=char('0'+k);
else if(k>=10 && k<=15)
arr[i]=char('A'+k-10);
num/=16;
++i;
}
k=num%16;
if( k>=0 && k<=9 )
arr[i]=char('0'+k);
else if(k>=10 && k<=15)
arr[i]=char('A'+k-10);
for(k=i;k>=0;--k)
cout<<arr[k];
cout<<endl;
}
return 0;
}
~~~
基础练习之特殊回文数——BASIC-9
最后更新于:2022-04-01 14:53:01
## 基础练习 特殊回文数
时间限制:1.0s 内存限制:512.0MB
问题描述
123321是一个非常特殊的数,它从左边读和从右边读是一样的。
输入一个正整数n, 编程求所有这样的五位和六位十进制数,满足各位数字之和等于n 。
输入格式
输入一行,包含一个正整数n。
输出格式
按从小到大的顺序输出满足条件的整数,每个整数占一行。
样例输入
52
样例输出
899998
989989
998899
数据规模和约定
1<=n<=54。
直观一点的,这个多加了一条剪枝,如果是五位数,在第一位数确定后 n-第一位数差不得大于 36,因为后面四位数最大为9999 所以不能大于36,其他几位数如是
~~~
// 特殊回文数
#include <iostream>
using namespace std;
int n;
int f[5],s[6];
bool judge(int ws,int no)
{
int i,sum=0;
if(ws==5)
{
if(no==1)
{
if(n-f[0]>36 || n-f[0]<0) return 0;
}
else if(no==2)
{
for(i=0;i<no;++i) sum+=f[i];
if(n-sum>27 || n-sum<0) return 0;
}
else if(no==3)
{
for(i=0;i<no;++i) sum+=f[i];
if(n-sum>18 || n-sum<0) return 0;
}
else if(no==4)
{
if(f[3]!=f[1]) return 0;
for(i=0;i<no;++i) sum+=f[i];
if(n-sum>9 || n-sum<0) return 0;
}
else
{
if(f[4]!=f[0]) return 0;
for(i=0;i<no;++i) sum+=f[i];
if(sum!=n) return 0;
}
}
else
{
if(no==1)
{
if(n-s[0]>45 || n-s[0]<0) return 0;
}
else if(no==2)
{
for(i=0;i<no;++i) sum+=s[i];
if(n-sum>36 || n-sum<0) return 0;
}
else if(no==3)
{
for(i=0;i<no;++i) sum+=s[i];
if(n-sum>27 || n-sum<0) return 0;
}
else if(no==4)
{
if(s[3]!=s[2]) return 0;
for(i=0;i<no;++i) sum+=s[i];
if(n-sum>18 || n-sum<0) return 0;
}
else if(no==5)
{
if(s[4]!=s[1]) return 0;
for(i=0;i<no;++i) sum+=s[i];
if(n-sum>9 || n-sum<0) return 0;
}
else
{
if(s[5]!=s[0]) return 0;
for(i=0;i<no;++i) sum+=s[i];
if(sum!=n) return 0;
}
}
}
int main()
{
int i,j,k;
while(cin>>n)
{
for(f[0]=1;f[0]<10;++f[0])
{
if(!judge(5,1)) continue;
for(f[1]=0;f[1]<10;++f[1])
{
if(!judge(5,2)) continue;
for(f[2]=0;f[2]<10;++f[2])
{
if(!judge(5,3)) continue;
for(f[3]=0;f[3]<10;++f[3])
{
if(!judge(5,4)) continue;
for(f[4]=0;f[4]<10;++f[4])
{
if(!judge(5,5)) continue;
for(i=0;i<5;++i)
cout<<f[i];
cout<<endl;
}
}
}
}
}
for(s[0]=1;s[0]<10;++s[0])
{
if(!judge(6,1)) continue;
for(s[1]=0;s[1]<10;++s[1])
{
if(!judge(6,2)) continue;
for(s[2]=0;s[2]<10;++s[2])
{
if(!judge(6,3)) continue;
for(s[3]=0;s[3]<10;++s[3])
{
if(!judge(6,4)) continue;
for(s[4]=0;s[4]<10;++s[4])
{
if(!judge(6,5)) continue;
for(s[5]=0;s[5]<10;++s[5])
{
if(!judge(6,6)) continue;
for(i=0;i<6;++i)
cout<<s[i];
cout<<endl;
}
}
}
}
}
}
}
return 0;
}
~~~
优化后的:
~~~
#include <iostream>
using namespace std;
int arr[7],i;
bool isreturn(int num)
{
// 判断它是五位还是六位
if(i%2==0)
{
if(arr[0]==arr[4] && arr[1]==arr[3]) return 1;
return 0;
}
else
{
if(arr[0]==arr[5] && arr[1]==arr[4] && arr[2]==arr[3]) return 1;
return 0;
}
}
bool isequal(int num,int n)
{
int s=0;
// 数分别存储
i=0;
while(num/10>0)
{
arr[i]=num%10;
num/=10;
s+=arr[i];
++i;
}
arr[i]=num;
s+=num;
return n==s;
}
int main()
{
int num,n;
while(cin>>n)
{
for(num=10000;num<1000000;++num)
if(isequal(num,n))
if(isreturn(num))
cout<<num<<endl;
}
return 0;
}
~~~
基础练习之回文数——BASIC-8
最后更新于:2022-04-01 14:52:58
## 基础练习 回文数
时间限制:1.0s 内存限制:512.0MB
问题描述
1221是一个非常特殊的数,它从左边读和从右边读是一样的,编程求所有这样的四位十进制数。
输出格式
按从小到大的顺序输出满足条件的四位十进制数。
~~~
//回文数
#include <iostream>
using namespace std;
int main()
{
int a,b,c,d;
for(a=1;a<10;++a)
{
for(b=0;b<10;++b)
{
for(c=0;c<10;++c)
{
if(c!=b) continue;
for(d=0;d<10;++d)
{
if(d!=a) continue;
cout<<a<<b<<c<<d<<endl;
}
}
}
}
return 0;
}
~~~
基础练习之特殊的数字——BASIC-7
最后更新于:2022-04-01 14:52:56
## 基础练习 特殊的数字
时间限制:1.0s 内存限制:512.0MB
问题描述
153是一个非常特殊的数,它等于它的每位数字的立方和,即153=1*1*1+5*5*5+3*3*3。编程求所有满足这种条件的三位十进制数。
输出格式
按从小到大的顺序输出满足条件的三位十进制数,每个数占一行。
~~~
// 特殊的数字
#include <iostream>
using namespace std;
int main()
{
int num,i,j,k;
for(num=100;num<1000;++num)
{
i=num/100;
k=num%10;
j=(num-i*100-k)/10;
if(num==i*i*i+j*j*j+k*k*k)
cout<<num<<endl;
}
return 0;
}
~~~
基础练习之杨辉三角形——BASIC-6
最后更新于:2022-04-01 14:52:54
## 基础练习 杨辉三角形
时间限制:1.0s 内存限制:256.0MB
问题描述
杨辉三角形又称Pascal三角形,它的第i+1行是(a+b)i的展开式的系数。
它的一个重要性质是:三角形中的每个数字等于它两肩上的数字相加。
下面给出了杨辉三角形的前4行:
1
1 1
1 2 1
1 3 3 1
给出n,输出它的前n行。
输入格式
输入包含一个数n。
输出格式
输出杨辉三角形的前n行。每一行从这一行的第一个数开始依次输出,中间使用一个空格分隔。请不要在前面输出多余的空格。
样例输入
4
样例输出
1
1 1
1 2 1
1 3 3 1
数据规模与约定
1 <= n <= 34。
基础练习之查找整除——BASIC-5
最后更新于:2022-04-01 14:52:52
## 基础练习 查找整数
时间限制:1.0s 内存限制:256.0MB
问题描述
给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个。
输入格式
第一行包含一个整数n。
第二行包含n个非负整数,为给定的数列,数列中的每个数都不大于10000。
第三行包含一个整数a,为待查找的数。
输出格式
如果a在数列中出现了,输出它第一次出现的位置(位置从1开始编号),否则输出-1。
样例输入
6
1 9 4 8 3 9
9
样例输出
2
数据规模与约定
1 <= n <= 1000。
~~~
// 查找整数
#include <iostream>
using namespace std;
int arr[1005];
int main()
{
int n,i,num;
bool isfind;
while(cin>>n)
{
isfind=false;
for(i=0;i<n;++i)
cin>>arr[i];
cin>>num;
for(i=0;i<n;++i)
if(num==arr[i])
{
isfind=true;
cout<<i+1<<endl;
break;
}
if(isfind==false)
cout<<"-1"<<endl;
}
return 0;
}
~~~