2018团体程序设计天梯赛 软件学院选拔赛
被你们虐了一下午的OJ是很辛苦的,让我们来看看题解吧。
L1-01 微妙的平衡
题目描述:
平衡是一种很微妙的状态。施加在一个物体上各种力,在什么情况下会使物体保持平衡呢。 假设对于一个力,我们由一个三维向量进行表示(x,y,z)。输入一个物体上的各种力,判断它是否处于平衡状态。
输入:
输入一个正整数n,接下来n行,分别输入第i个力的x,y,z
输出:
如果该物体保持平衡,输出Balance,否则输出Imbalance
样例输入1:
3
1 2 3
-1 -1 -1
0 1 1
样例输出1:
Imbalance
样例输出2:
4
1 2 3
0 -2 1
2 5 -4
-3 -5 0
样例输出2:
Balance
题目大意:
输入的xyz的和等于0即为平衡,不为0则不平衡。
参考思路:
直接将输入的XYZ相加,判断是否等于0即可。
参考代码:
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
int x=0,y=0,z=0;
while(n--){
int a,b,c;
cin>>a>>b>>c;
x+=a; y+=b; z+=c;
}
if(x==0&&y==0&&z==0) cout<<"Balance";else cout<<"Imbalance";
return 0;
}
L1-02 输出SCNU
题目描述:
给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按“SCNUSCNU...”这样的顺序输出,并忽略其它字符。当然,四种字符(不区分大小写)的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按SCNU的顺序打印,直到所有字符都被输出。
输入格式:
输入在一行中给出一个长度不超过1000的、仅由英文字母构成的非空字符串。
输出格式:在一行中按题目要求输出排序后的字符串。题目保证输出非空。
输入样例:
wqevzSbbbceeNtytuyyySyyCyNiuoSpNppUasddNaSfnfnf
输出样例:
SCNUSCNUSNUSNSNN
题目大意:
分别统计S,C,N,U的数量然后按顺序输出。
参考思路:
统计SCNU的数量到数组后,不停做循环直到清空数组。
参考代码:
#include<iostream>
#include<string>
#include<functional>
#include<algorithm>
using namespace std;
int main()
{
strings;
inta[4]={0};
getline(cin,s);
transform(s.begin(), s.end(), s.begin(), ::toupper);
for(inti=0;i<s.size();i++)
switch (s[i])
{
case 'S' : a[0]++; break;
case 'C' : a[1]++; break;
case 'N' : a[2]++; break;
case 'U' : a[3]++; break;
}
intflag=1;
while(flag)
{
flag=0;
if(a[0]!=0){cout<<"S"; a[0]--;}
if(a[1]!=0){cout<<"C"; a[1]--;}
if(a[2]!=0){cout<<"N"; a[2]--;}
if(a[3]!=0){cout<<"U"; a[3]--;}
for(int i=0;i<=3;i++)
if(a[i]!=0) flag=1;
}
return0;
}
L1-03又是素数
题目描述:
CGY对于素数有着迷之喜爱,而有这么一个素数,是一个收到魔法影响的素数,据说念出它的人。。。。。
输入:
无
输出:
输出第1226564个素数
Hint:
该题没有样例输入输出
参考思路:
用暴力解法求出第1226564个素数后直接输出它就好了,如果社会工程学学得好的话可以直接输出。
参考代码:
#include<iostream>
using namespace std;
int main(){
cout<<19260817;
return0;
}
L1-04 小书童的密码
题目描述:
某蒟蒻迷上了“小书童”,有一天登陆时忘记密码了(他没绑定邮箱or手机),于是便把问题抛给了神犇你。
蒟蒻虽然忘记密码,但他还记得密码是由一串字母组成。且密码是由一串字母每个向后移动n为形成。z的下一个字母是a,如此循环。他现在找到了移动前的那串字母及n,请你求出密码。(均为小写)
输入:
第一行n
第二行:未移动前的一串字母(长度小于100)
输出:
一行,该蒟蒻的密码
样例输入:
1
qwe
样例输出:
rxf
参考思路:
这道题就是凯撒密码,输入的n是向后移的位置。
参考代码:
#include <stdio.h>
int main()
{
charin[100];
int n,j;
scanf("%d%s", &n, in);
for(j= 0; in[j] != '\0'; j++)
putchar((in[j]-'a'+n)%26+'a');
return0;
}
L1-05 出租
题目描述:
某电线杆上出现了如下的出租信息:
升景坊单间短期出租四个月,550/月(水电煤公摊,网费35元/月)。空调,卫生间,厨房齐全。屋内均为IT行业人士,喜欢安静,所以要求前来出租者最好为同行或者刚毕业的年轻人,爱干净、安静.
有意者电联
联系人:成先生
联系方式:请阅读代码
int[] arr = new int[]{8,2,1,0,3};
int[] index = new int[]{2,0,3,2,4,0,1,3,2,3,3};
String tel = "";
for(int i : index){
tel+= arr[i];
}
System.out.println("Tel:"+tel);
有人想租房,却不知道电话号码到底是多少。其实这段代码很简单,index数组就是arr数组的下标,index[0]= 2 对应 arr[2]=1,index[1]=0 对应 arr[0]=8 ,index[2]=3 对应 arr[3]=0,以此类推,很容易得到电话号码为18013820100
WXY觉得这十分有创意,但是她不会编程,于是她找到了你,希望你生成该程序的前两行(因为后面都是一样的)的程序。
输入:
一行电话号码(长度不多于20)
输出:
两行分别输出代码的前两行,为了统一格式,arr中的数字必须按照递减顺序给出
输入样例:
18013820100
输出样例:
int[] arr = new int[]{8,3,2,1,0};
int[] index = new int[]{3,0,4,3,1,0,2,4,3,4,4};
参考思路:
arr数组内的数字是从大到小的出现在电话号码内的数字,index就是对应这一位电话号码的数字在arr内的下标。
参考代码:
#include<iostream>
#include<vector>
#include<fstream>
using namespace std;
int main()
{
stringstr;
cin>> str;
intnumber[10] = {0};
for(int i = 0; str[i] != '\0'; i++) number[str[i] - '0']++;
vector<int> vec;
cout<< "int[] arr = new int[]{";
for(int i = 9; i>=0; i--)
{
if(number[i] != 0)
{
if (!vec.empty()) cout << ",";
cout << i;
vec.push_back(i);
}
}
cout<< "};" << endl;
cout<< "int[] index = new int[]{";
for(int i = 0; str[i] != '\0'; i++)
{
int size = vec.size();
for (int j = 0; j < size; j++)
{
if (vec[j] == str[i] - '0')
{
if (i != 0) cout <<",";
cout << j;
break;
}
}
}
cout<< "};" << endl;
return0;
}
L1-06 连杀称号
题目描述:
很多童鞋喜欢玩一些lol、dota这类游戏,这类游戏有一个特点,在你不死的情况下连续杀人会有不同称号。
下面输入一组字符,其中只包含K和D,K代表杀敌,D代表死亡,求他最高称号。
0/1/2 - You are a Foolish Man
3 - You are on a Killing Spree
4 - You are Dominating
5 - You have a Mega-Kill
6 - You are Unstoppable
7 - You are Wicked Sick
8 - You have a M-m-m-m....Monster Kill
9 - You are Godlike
10+ - You are Beyond Godlike (Somebody killhim!)
输入:
一串仅由K和D构成的字符串s,s的长度小于300
输出:
该玩家的最高称号
样例输入:
KKKDKKDDDKKKK
样例输出:
You are Dominating
参考思路:
很简单,就是找最长有多少个连续的K。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include<fstream>
using namespace std;
const int maxn=305;
string s;
int main(){
cin>>s;
intl=s.length(),maxx=0;
for(inti=0;i<l;){
intkill=0;
if(s[i]=='K'){
for(;s[i]=='K';i++)kill++;
maxx=max(maxx,kill);
continue;
}
i++;
}
switch(maxx){
case0:
case1:
case2:cout<<"You are a Foolish Man"<<endl; break;
case3:cout<<"You are on a Killing Spree"<<endl; break;
case4:cout<<"You are Dominating"<<endl; break;
case5:cout<<"You have a Mega-Kill"<<endl; break;
case6:cout<<"You are Unstoppable"<<endl; break;
case7:cout<<"You are Wicked Sick"<<endl; break;
case8:cout<<"You have a M-m-m-m....Monster Kill"<<endl;break;
case9:cout<<"You are Godlike"<<endl; break;
case10:cout<<"You are Beyond Godlike (Somebody killhim!)"<<endl; break;
}
if(maxx>10)cout<<"You are Beyond Godlike (Somebody killhim!)"<<endl;
return0;
}
L1-07 输出字符串
题目描述:
请你编写程序,根据输入的长度 N 和重复次数 M 循环输出形如 AA..ABB..B....ZZ..Z 的字符串。
输入
输入两个正整数 N≤10000 和 M≤10 。
输出
输出你的字符串。
样例输入
4 1
4 2
52 2
3 10
27 10
55 2
样例输出
ABCD
AABB
AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ
AAA
AAAAAAAAAABBBBBBBBBBCCCCCCC
AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZAAB
提示
会有多组输入,以文件尾(EOF)表示结束。
参考思路:
按照题目要求输出字符串,需要注意的是如果长度过大需要从A开始重新输出。
参考代码:
#include <stdio.h>
int main() {
int N,M;
while(scanf("%d%d", &N, &M)!=EOF) {
for (int i = 0; i < N; ++i) {
printf("%c",'A' + i / M % 26);
}
printf("\n");
}
return0;
}
L1-08 神奇的幻方
题目描述:
幻方是一种很神奇的N*N矩阵:它由数字1,2,3, … … ,N*N构成,且每行、每列及两条对角线上的数字之和都相同。
当N为奇数时,我们可以通过以下方法构建一个幻方:
首先将 1写在第一行的中间。之后,按如下方式从小到大依次填写每个数(K= 2,3, … ,N*N ):
1.若 (K-1)在第一行但不在最后一列,则将填在最后一行,(K-1)所在列的右一列;
2.若 (K-1)在最后一列但不在第一行,则将填在第一列,( K-1)所在行的上一行;
3.若 ( K-1)在第一行最后一列,则将填在(K-1)的正下方;
4.若 (K-1)既不在第一行,也不在最后一列,如果( K-1)的右上方还未填数,
则将 K填在( K-1)的右上方,否则将填在( K-1)的正下方。
现给定N,请按上述方法构造N?N的幻方。
输入:
输入整数n(1<=n<=39且位奇数)幻方的边长
输出:
输出文件为n行,输出按照上述方法构造的幻方,两个整数间用单个空格隔开
样例输入:
3
样例输出:
8 1 6
3 5 7
4 9 2
参考思路:
难度一般的纯模拟题目,每填一个数字,就保存一下这个数的坐标。填下个数的时候,刚才保存的坐标就是 (K - 1) 的坐标。跟据此坐标判断 K 应该填到哪个位置。其中,填第一个数的时候需要特判。
参考代码:
#include <iostream>
using namespace std;
int a[40][40] = { 0 };
int main() {
int n;
cin>> n;
intstep = 1;
intposx, posy;
while(step <= n * n) {
if(step == 1)
a[posx = 1][posy = n / 2 + 1] = step++;
else if (posx == 1 && posy != n)
a[posx = n][++posy] = step++;
else if (posx != 1 && posy == n)
a[--posx][posy = 1] = step++;
else if (posx == 1 && posy == n)
a[++posx][posy] = step++;
else if (posx != 1 && posy != n)
if (a[posx - 1][posy + 1] == 0)
a[--posx][++posy] = step++;
else
a[++posx][posy] = step++;
}
for(int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
cout << a[i][j] << " ";
cout << endl;
}
}
L2-01 成绩排序
题目描述:
在学院里,有许多人的成绩,而排名对于学生来说是十分重要的。期末考试结束了,有一波新鲜的成绩来到了教务处,然而今年的人数十分庞大,在录入成绩后,需要进行一次排序,具体排序规则如下。
首先是学生的年级,高年级的人在前。
其次是学生的成绩,成绩高的在前。
最后是学号,学号小的在前。
输入:
输入n表示学生人数(n<=10000),接下来n行,每行从前到后分别为学生年级,学生学号(位数不明),学生姓名(无空格),学生分数(保留两位小数)
输出:
输出排序后的成绩单,一行一人,格式与输入格式相同。
样例输入:
6
2016 201620050 Whengcheng 3.33
2015 201533333 Cuoyan 2.98
2015 201520081 Wenzou 3.15
2015 201511122 Chenyao 2.98
2015 20151111 Chenyao 2.98
2013 201322222 Tanjiushi 2.50
样例输出:
2013 201322222 Tanjiushi 2.50
2015 201520081 Wenzou 3.15
2015 20151111 Chenyao 2.98
2015 201511122 Chenyao 2.98
2015 201533333 Cuoyan 2.98
2016 201620050 Whengcheng 3.33
参考思路:
很简单的结构体排序,直接告诉了排序方法,需要注意的是需要使用优化后的排序算法而不是使用一般的排序算法。
参考代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<functional>
#include<algorithm>
using namespace std;
typedef struct Stu{
intgrade;
stringnumber;
stringname;
doublescore;
} student;
bool cmp(student a,student b);
int main(){
int n;
cin>>n;
student a[10005];
for(int i=1;i<=n;i++)cin>>a[i].grade>>a[i].number>>a[i].name>>a[i].score;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) {cout<<a[i].grade<<""<<a[i].number<<" "<<a[i].name<<""; printf("%.2f\n",a[i].score); }
return0;
}
bool cmp(student a,student b){
if(a.grade<b.grade) return true; else
if(a.grade>b.grade) return false;
if(a.score > b.score) return true; else
if(a.score <b.score) return false;
if(a.number.size() < b.number.size())return true; else
if(a.number.size() > b.number.size() ) return false;
if(a.number < b.number ) return true; else return false;
}
L2-02 友军之围
题目描述:
某区域爆发了战争,其中有多股势力参战,然而他们的通讯设备十分不先进,消息也十分落后。指挥官只知道自己和谁是友军,而友军的友军却......因此经常会出现友军包围了友军进行一波痛击,为了防止这种事情发生,指挥官找到了你,收集了战场上任意两只部队友军关系,希望你开发系统进行判断。在默认不是友军就是敌军的情况下。
输入:
第一行输入n和m,n表示有几只部队,m表示有几种关系,接下来m行,分别输入两只部队的番号x,y(1<=x,y<=n<=2000)表示x和y是友军
接来下输入正整数T,表示有T组数据需要确认,之后T行,分别输入需要查询的两只部队的番号
输出:
T行,分别给出待查询的部队间的关系,如果是友军则输出"Ceasefire!",否则输出"Openfire!"
样例输入:
8 3
1 2
3 4
2 6
4
1 4
1 6
7 8
2 4
样例输出:
Open fire!
Ceasefire!
Open fire!
Open fire!
参考思路:
题目本意是使用并查集,但是由于数据问题而且并没有进行过多地运算,把它当做一个边为1的图来做也是可以的。需要注意的是并查集一开始初始化不应该是都等于-1,而是自己的父亲是自己。如果使用图论使用Floyd也可以实现该题的要求。甚至,数组标记法也是可以完成这道题目的。
参考答案(并查集优化版本):
#include <iostream>
#include <cstdio>
using namespace std;
int fa[10005],n,m;
int father(int k)
{
intkk=k;
while(fa[kk]!=kk)
kk=fa[kk];
int i;
while(fa[k]!=k)
{
i=k;
k=fa[k];
fa[i]=kk;
}
returnkk;
}
int main()
{
for(int i=0;i<10005;i++)
fa[i]=i;
scanf("%d%d",&n,&m);
while(m--)
{
int ask,x,y;
scanf ("%d%d",&x,&y);
fa[father(x)]=father(y);
}
scanf("%d",&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
if(father(x)==father(y)) printf("Ceasefire!\n");
else printf ("Open fire!\n");
}
return0;
}
参考答案(数组标记法,本质上和并查集有异曲同工之处,来自15梁炯壮):
#include <iostream>
usingnamespacestd;
intmain()
{
inti,n,m,x,y,j,t;
inttemp;
inta[2001];
cin>>n>>m;
intsign=0;
for(i=0;i<=n;i++) a[i]=0;
for(i=0;i<m;i++){
cin>>x>>y;
if(a[x]==0&&a[y]==0){
sign++;
a[x]=sign;
a[y]=sign;
}
elseif(a[x]==a[y])continue;
elseif(a[x]!=0&&a[y]==0)a[y]=a[x];
elseif(a[x]==0&&a[y]!=0)a[x]=a[y];
elseif(a[x]!=0&&a[y]!=0){
temp=a[y];
for(j=1;j<=2001;j++){
if(a[j]==temp){
a[j]=a[x];
}
}
}
}
cin>>t;
for(i=0;i<t;i++){
cin>>x>>y;
if(a[x]!=a[y]||(a[x]==0&&a[y]==0&&x!=y))cout<<"Openfire!\n";
elsecout<<"Ceasefire!\n";
}
return0;
}
L2-03 拦截导弹
题目描述:
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入:
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),导弹数量不大于2000
输出:
输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例输入:
389 207 155 300 299 170 158 65
样例输出:
6
2
参考思路:
这道题是一道经典的动态规划题目:最长不上升子序列。由于数据并不大,所以即使是使用没有优化的算法也是可以通过的。洛谷上题解去有很多很多新奇的做法,在此仅提供最简单的做法。第二个问题其实本质上就是贪心,只要求有多少组
参考代码( O(n^2) ):
#include<cstdio>
#include<algorithm>
using namespace std;
int n,ans1,ans2,f[100001],a[100001];
int main(){
while(scanf("%d",&a[++n])!=EOF);
n--;
for(int i=n;i>=1;i--){
f[i]=1;
for(int j=i+1;j<=n;j++){
if(a[j]<=a[i])
f[i]=max(f[i],f[j]+1);
}
ans1=max(ans1,f[i]);
}
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++){
if(a[j]<a[i])
f[i]=max(f[i],f[j]+1);
}
ans2=max(ans2,f[i]);
}
printf("%d\n%d\n",ans1,ans2);
}
L2-04 倒水
题目描述:
一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水。接着~~CC发现瓶子实在太多了,于是他决定保留不超过K个瓶子。每次他选择两个当前含水量相同的瓶子,把一个瓶子的水全部倒进另一个里,然后把空瓶丢弃。(不能丢弃有水的瓶子)
显然在某些情况下CC无法达到目标,比如N=3,K=1。此时CC会重新买一些新的瓶子(新瓶子容量无限,开始时有1升水),以到达目标。
现在CC想知道,最少需要买多少新瓶子才能达到目标呢?
输入:
第一行一个整数T,表示有T(T<10)组数据。
接着T行,每行两个正整数,N,K(1<=N<=10^9,K<=1000)。
输出:
一个非负整数,表示最少需要买多少新瓶子。
样例输入:
3
3 1
13 2
1000000 5
样例输出:
1
3
15808
参考思路:
纯二进制题目。因为所有的水都是由两份相同的水合并而成的,因此每瓶水的体积一定是2^i,(i∈N) 升。最后保留k个瓶子,那么最后总的升数的二进制表示中,1的个数一定<=k。
分析一下样例(来自洛谷Shan_Xian):
n=3 m=1
先把n转化为二进制,得11。
这就说明n个瓶子能合并成2个瓶子,但不满足题意,所以应该再减少一个瓶子。
二进制11想要将它变成只含有一个1的,那么就+1,得100,所以答案就是1。也就是说先从右往左数第一个1的位置+1,再加上其二的方幂,然后继续找,直至满足题意。
再看第二个样例:
n=13 m=2
还是将n转化为二进制,得1101
说明n个瓶子能合并成3个瓶子。
按第一题得到的规律,先从右往左数第一个1的位置+1,再加上其二的方幂,然后继续找,直至满足题意。
第三个样例也是如此:
n=1000000 m=5
将n转化为二进制,得11110010 0100 0000
仍按第一题得到的规律,先从右往左数第一个1的位置+1,再加上其二的方幂,然后继续找,直至满足题意。
参考代码(来自洛谷pantw):
//__builtin_popcount用来求一个二进制数中1的数量。
#include <cstdio>
int n, k, ans;
int main() {
int s;
scanf("%d",&s);
while(s--){
ans=0;
scanf("%d%d", &n, &k);
while(__builtin_popcount(n)> k) ans += n & -n, n += n & -n;
printf("%d\n", ans);
}
return0;
}
参考代码(优先队列):
#include<stdio.h>
#include<queue>
#include<iostream>
using namespace std;
priority_queue<int, vector<int>,greater<int> > q; //优先队列,先出小的元素
int pow2[33];//用来记录2的i次方是几,比如pow2[i]表示2的i次方
int sum;//需要买的瓶子数
int t,n,k;
void calcu()//计算瓶子数,每执行一次就把最小的两瓶倒在了一起
{
cout<<q.size()<<" "<<k<<endl;
if(q.size()==k)//当队列里的瓶子数量等于k,达到题意
{
return;
}
//下面取出两个最小的瓶子
intu=q.top();
q.pop();
intv=q.top();
q.pop();
//把取出的两瓶,想办法倒在一起;
sum+=pow2[v]-pow2[u];//关键的计算
//这样就把2^u 的瓶子变成了2^v 那么大,然后倒在一起,放回队列里
q.push(v+1);
calcu();//继续计算
}
int main()
{
scanf("%d",&t);
pow2[0]=1; //2的0次方等于1;
for(int i=1;i<=30;i++)
pow2[i]=2*pow2[i-1];
while(t--)
{
cout<<q.size();
while(!q.empty())
{
q.pop();
}
scanf("%d%d",&n,&k);
for(int i=0; n; i++)//当n等于0时,说明已经没有可以再倒在一起的了
{
if(n%2==1)//有奇数个相等水量的瓶子,余一个不能倒的,暂时放在队列里
{
q.push(i);//剩余一个2的i次方的水瓶
}
n/=2;//将偶数个瓶子两两倒在一起,瓶子减少一半
}
sum=0;
calcu();
printf("%d\n",sum);
}
return0;
}
L3-01 二次方程
题目描述:
对于给定的L,找到一个不小于L的n与m满足关系式 2m(m+1)=n(n+1)
输入:
该题包含多组数据,第一行输入T(1<=T<=1000),接下来T行每行给出一个L,输出最小的满足条件的n(1<=L<=n<=10^190)
输出:
一共T行,每行输出对于给定的L,输出满足条件的最小的n,如果在范围内找不到n,输出-1
样例输入:
3
1
4
21
样例输出:
3
20
119
参考思路:
本题作为压轴题,本质上是一道归纳题。有很多方法可以得到n数列的公式,笔者在一个小时的手工计算下,得出的公式是n[i]=(n[i-1]-n[i-3])*6+b[i-4],如果有那位dalao有一系列做这类题的方法,欢迎告诉我。另外,本题数据较大,需要使用高精度计算。
参考答案(JAVA):
import java.util.*;
import java.math.*;
public class Main{
private static Scanner cin;
publicstatic void main(String[] args) {
cin = new Scanner(System.in);
int n=cin.nextInt();
BigInteger []s=new BigInteger [1005];
BigInteger []a=new BigInteger [1000];
String x="1";
for(int i=1;i<=190;i++) x=x+"0";
for(int i=1;i<=n;i++) {
s[i]=cin.nextBigInteger();
}
int i=5;
a[0]=new BigInteger("6");
a[1]=new BigInteger("3");
a[2]=new BigInteger("20");
a[3]=new BigInteger("119");
a[4]=new BigInteger("696");
while(true) {
a[i]=(a[i-1].subtract(a[i-3]) ).multiply(a[0]).add(a[i-4]);
if(a[i].compareTo(new BigInteger(x))>0) break;
i++;
}
for(int j=1;j<=n;j++)
for(int k=1;k<=i;k++) {
if( a[k].compareTo(s[j]) >=0 ) {
System.out.print(a[k]+"\n");
break;
}
}
}
}
参考代码(c++):
//比赛不建议使用我这个完整的模板,太大了.
#include<iostream>
#include<cstring>
#include<string>
#include<iomanip>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAXN 9999
#define MAXSIZE 100
#define DLEN 4
class BigNum
{
public:
inta[500]; //可以控制大数的位数
intlen; //大数长度
public:
BigNum(){ len = 1;memset(a,0,sizeof(a)); } //构造函数
BigNum(const int); //将一个int类型的变量转化为大数
BigNum(const char*); //将一个字符串类型的变量转化为大数
BigNum(const BigNum &); //拷贝构造函数
BigNum&operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
friendistream& operator>>(istream&, BigNum&); //重载输入运算符
friendostream& operator<<(ostream&, BigNum&); //重载输出运算符
BigNumoperator+(const BigNum &) const; //重载加法运算符,两个大数之间的相加运算
BigNumoperator-(const BigNum &) const; //重载减法运算符,两个大数之间的相减运算
BigNumoperator*(const BigNum &) const; //重载乘法运算符,两个大数之间的相乘运算
BigNumoperator/(const int &) const; //重载除法运算符,大数对一个整数进行相除运算
BigNumoperator^(const int &) const; //大数的n次方运算
int operator%(const int &) const; //大数对一个int类型的变量进行取模运算
bool operator>(const BigNum& T)const; //大数和另一个大数的大小比较
bool operator>(const int& t)const; //大数和一个int类型的变量的大小比较
voidprint(); //输出大数
};
BigNum::BigNum(const int b) //将一个int类型的变量转化为大数
{
intc,d = b;
len =0;
memset(a,0,sizeof(a));
while(d > MAXN)
{
c= d - (d / (MAXN + 1)) * (MAXN + 1);
d= d / (MAXN + 1);
a[len++] = c;
}
a[len++] = d;
}
BigNum::BigNum(const char*s) //将一个字符串类型的变量转化为大数
{
intt,k,index,l,i;
memset(a,0,sizeof(a));
l=strlen(s);
len=l/DLEN;
if(l%DLEN)
len++;
index=0;
for(i=l-1;i>=0;i-=DLEN)
{
t=0;
k=i-DLEN+1;
if(k<0)
k=0;
for(int j=k;j<=i;j++)
t=t*10+s[j]-'0';
a[index++]=t;
}
}
BigNum::BigNum(const BigNum & T) :len(T.len) //拷贝构造函数
{
int i;
memset(a,0,sizeof(a));
for(i= 0 ; i < len ; i++)
a[i] = T.a[i];
}
BigNum & BigNum::operator=(const BigNum& n) //重载赋值运算符,大数之间进行赋值运算
{
int i;
len =n.len;
memset(a,0,sizeof(a));
for(i= 0 ; i < len ; i++)
a[i] = n.a[i];
return*this;
}
istream& operator>>(istream &in, BigNum & b) //重载输入运算符
{
charch[MAXSIZE*4];
int i= -1;
in>>ch;
intl=strlen(ch);
intcount=0,sum=0;
for(i=l-1;i>=0;)
{
sum = 0;
int t=1;
for(int j=0; j<4&&i>=0; j++,i--,t*=10)
{
sum+=(ch[i]-'0')*t;
}
b.a[count]=sum;
count++;
}
b.len=count++;
returnin;
}
ostream& operator<<(ostream&out, BigNum& b) //重载输出运算符
{
int i;
cout<< b.a[b.len - 1];
for(i= b.len - 2 ; i >= 0 ; i--)
{
cout.width(DLEN);
cout.fill('0');
cout << b.a[i];
}
returnout;
}
BigNum BigNum::operator+(const BigNum & T)const //两个大数之间的相加运算
{
BigNumt(*this);
inti,big; //位数
big =T.len > len ? T.len : len;
for(i= 0 ; i < big ; i++)
{
t.a[i] +=T.a[i];
if(t.a[i]> MAXN)
{
t.a[i + 1]++;
t.a[i] -=MAXN+1;
}
}
if(t.a[big] != 0)
t.len = big + 1;
else
t.len = big;
returnt;
}
BigNum BigNum::operator-(const BigNum & T)const //两个大数之间的相减运算
{
inti,j,big;
boolflag;
BigNumt1,t2;
if(*this>T)
{
t1=*this;
t2=T;
flag=0;
}
else
{
t1=T;
t2=*this;
flag=1;
}
big=t1.len;
for(i= 0 ; i < big ; i++)
{
if(t1.a[i] < t2.a[i])
{
j = i + 1;
while(t1.a[j] == 0)
j++;
t1.a[j--]--;
while(j > i)
t1.a[j--] += MAXN;
t1.a[i] += MAXN + 1 - t2.a[i];
}
else
t1.a[i] -= t2.a[i];
}
t1.len= big;
while(t1.a[t1.len - 1] == 0 && t1.len > 1)
{
t1.len--;
big--;
}
if(flag)
t1.a[big-1]=0-t1.a[big-1];
returnt1;
}
BigNum BigNum::operator*(const BigNum & T)const //两个大数之间的相乘运算
{
BigNumret;
inti,j,up;
inttemp,temp1;
for(i= 0 ; i < len ; i++)
{
up= 0;
for(j = 0 ; j < T.len ; j++)
{
temp = a[i] * T.a[j] + ret.a[i + j] + up;
if(temp > MAXN)
{
temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else
{
up = 0;
ret.a[i + j] = temp;
}
}
if(up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while(ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
returnret;
}
BigNum BigNum::operator/(const int & b)const //大数对一个整数进行相除运算
{
BigNumret;
inti,down = 0;
for(i= len - 1 ; i >= 0 ; i--)
{
ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
}
ret.len = len;
while(ret.a[ret.len- 1] == 0 && ret.len > 1)
ret.len--;
returnret;
}
int BigNum::operator %(const int & b)const //大数对一个int类型的变量进行取模运算
{
inti,d=0;
for (i= len-1; i>=0; i--)
{
d= ((d * (MAXN+1))% b + a[i])% b;
}
returnd;
}
BigNum BigNum::operator^(const int & n)const //大数的n次方运算
{
BigNumt,ret(1);
int i;
if(n<0)
exit(-1);
if(n==0)
return 1;
if(n==1)
return *this;
intm=n;
while(m>1)
{
t=*this;
for( i=1;i<<1<=m;i<<=1)
{
t=t*t;
}
m-=i;
ret=ret*t;
if(m==1)
ret=ret*(*this);
}
returnret;
}
bool BigNum::operator>(const BigNum & T)const //大数和另一个大数的大小比较
{
intln;
if(len> T.len)
return true;
elseif(len == T.len)
{
ln= len - 1;
while(a[ln] == T.a[ln] && ln >= 0)
ln--;
if(ln >= 0 && a[ln] > T.a[ln])
return true;
else
return false;
}
else
return false;
}
bool BigNum::operator >(const int & t)const //大数和一个int类型的变量的大小比较
{
BigNumb(t);
return*this>b;
}
void BigNum::print() //输出大数
{
int i;
cout<< a[len - 1];
for(i= len - 2 ; i >= 0 ; i--)
{
cout.width(DLEN);
cout.fill('0');
cout << a[i];
}
cout<< endl;
}
BigNum a,b[1000];
int main(){
//freopen("test1.in","r",stdin);
//freopen("test1.out","w",stdout);
intn,m=0;
cin>>n;
b[0]=BigNum(10)^190;
b[1]=BigNum(3);
b[2]=BigNum(20);
b[3]=BigNum(119);
b[4]=BigNum(696);
m=5;
//cout<<b[0]<<endl;
for(int i=2;;i++){
//cout<<b[i-1]<<endl;
if(i<=4) continue;
m++;
b[i]=(b[i-1]-b[i-3])*BigNum(6)+b[i-4];
if(b[i]>b[0]) break;
}
//cout<<n<<endl;
for(int i=1;i<=n;i++){
cin>>a;
int p=1;
for(int j=1;j<=m;j++)
if((b[j]>a)||(b[j]>a==false)&&(a>b[j]==false)) {cout<<b[j]<<endl;p=0; break;}
if(p) cout<<-1<<endl;
}
return0;
}
比赛复盘和总结
由于天梯赛是按照三层难度来进行做题的,因此这次预选赛也按照三层难度出题。由于出题人(本人)在数据上的校对不够严格,导致比赛过程中有两道题的数据有误,在此向比赛选手做出道歉。总的来说这次L1题目的难度偏大,而且我没有想到同学们在字符串处理上会有这么大的问题。L2题目的难度相对于正式比赛过于简单,一眼就能看出做法的题目(第四题除外),而L3的一道题目完全就是不打算有人做出来而出的。总的来说,这次比赛的题目难度适中,主要是L1和L2难度互补了,而我相信选拔出来的人也同样是很有实力的,决赛加油!