A题:塔
一个层高为n的字母塔的定义为:
共n行,由字母组成的等腰三角形。
塔顶为第一层,且只有一个大写字母A;下面每一层都比上面一层多两个字母。
每一层都是左右对称。
对于第i层,前i个字母由大写字母表中A~第i个字母顺序组成。
为了稳住局面,样例给出了层高为5的字母塔,请你输出层高26的字母塔。
样例:
#include <iostream>
using namaspace std;
/***********************************
观察题目样例给出的高为5层的塔,可以得出以下几个规律
对于一个高为n层的塔而言,首先设最上面一层(顶层)为第一层。
1. 对于第i层而言,其字符的排列规律为:大写字母表中从第1个字符(A)~第i个字符,后又倒序从第i-1个字符~第1个字符(A)。
2. 第1~n-1层每层前都有空格,具体而言,对于第i行,字符前面的空格个数为n-i个。
找出以上规律后,我们就可以根据这些规律构造出答案:层高26的塔。
TIPS:
大写字母'A'为大写字母表第一个字符
对于大写字母表中第i个字符,可以使用'A'+i-1得到。
例如:第5个字符为'E',亦即为:'A'+5-1
***********************************/
int main() {
char c1;
int n = 26; //设定塔的层数为26
int i, j;
for (i = 1; i <= n; i++) { //对塔每一层按照规律进行构造。
//首先进行输出空格的操作:对于第i行,字符前面的空格个数为n-i个。
for (j = 1; j <= n-i; j++)
printf(" ");
for (j = 1; j <= i; j++) { //按照规律1,输出第1~第i个大写字母。
c1 = j + 'A' - 1; //第j个大写字母为'A'+j-1
printf("%c", c1); //输出第j个大写字母
}
for (j = i-1; j >= 1; j--) {//按照规律1,输出第i-1~第1个大写字母,注意是倒序
c1 = j + 'A' - 1;
printf("%c", c1);
}
printf("\n");//第i行输出结束,进行换行。
}
return 0;
}
B题:日记加密
加密的方式很简单:对于一串字符串,如果其中有l,i,n,k,e这五个字母当中的任意一个,都会在这后面加上bt再加上原来的字母,如love就会加密成lbtlovebte。
下面给出日记的第一页内容,根据日记内容进行解密。
ibti lbtlovebte lbtlibtinbtnkbtkebtezbas jebte dosadnbtna ovakbtkebtemibtijaxaszxdbtddbtddbtddbtddbtddbtd
我的思路比较麻烦,删除vector数组的内容
#include <iostream>
#include <vector>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
char ps[120] = "ibti lbtlovebte lbtlibtinbtnkbtkebtezbas jebte dosadnbtna ovakbtkebtemibtijaxaszxdbtddbtddbtddbtddbtddbtd";
vector<char> p{ ps,ps+120 };
vector<char>::iterator itr = p.begin();
for(itr;itr!=p.end();itr++)
{
if (*itr == 'l' || *itr == 'i' || *itr == 'n' || *itr == 'k' || *itr == 'e')
{
itr = p.erase(itr, itr + 3);//删除
}
}
for (itr=p.begin(); itr != p.end(); itr++) {
cout << *itr;
}//输出i love linkezbas je dosadna ovakemijaxaszxdbtddbtddbtddbtddbtddbtd
}
其他思路
1、遇到l、i、n、k、e时,其后面必是跟着bt和这个字母本身。
所以可以读取字符串后,for循环按顺序枚举并输出每个字母,但遇到l、i、n、k、e时不要输出其后面的三个字母即可。
#include <iostream>
using namespace std;
int main()
{
char s[] = "ibti lbtlovebte lbtlibtinbtnkbtkebtezbas jebte dosadnbtna ovakbtkebtemibtijaxaszxdbtddbtddbtddbtddbtddbtd";
int n = strlen(s);
for (int i = 0; i < n; i++) {
printf("%c", s[i]);
if (s[i] == 'l' || s[i] == 'i' || s[i] == 'n' || s[i] == 'k' || s[i] == 'e')
i += 3;
}
}
/*
#include <iostream>
#include<string>
using namespace std;
int main()
{
string str;
getline(cin, str);
for (int i = 0; i < str.size(); i++) {
cout << str[i];
if (str[i] == 'l' || str[i] == 'i' || str[i] == 'n' || str[i] == 'k' || str[i] == 'e') {
i += 3;
}
}
}
*/
2、使用getchar()按字母读取字符串,每读取到一个字母时就将其输出。
但遇到l、i、n、k、e时,使用三次getchar()读取后面加密多出来的字符,且不输出即可跳过
M题:签到题
如果输入的值是1,则回复ADPC。
否则的话,输出12345
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
if (a == 1)
cout << "ADPC";
else
cout << "12345";
}
J题 鸭题
有一个A×B 的大澡盆,还有若干个a×b 的长方形小鸭子,澡盆里最少放几只鸭子后,便无法再向其中放入更多的鸭子?鸭子很倔强,不能旋转成 b\times ab×a ,也不能重叠放置。
Input:四个整数,分别表示 A,B,a,b。1<=a<=A<=30000 1<=b<=B<=30000
Output:最少能放几只鸭子。
输入20 10 9 2,输出3
水平方向上两个长方形的间隔小于a(取a-0.000001),竖直方向两个长方形的间隔小于b(取b-0.000001),摆放时第一个矩形不要放在最左边,而是放在距离左边界a-0.000001的位置。
故水平方向最多可以摆(A-(a-0.000001))/(a+a-0.000001)
竖直方向最多可以摆(B-(b-0.000001))/(b+b-0.000001)
向上取整再相乘即可
#include <iostream>
#include<cmath>
using namespace std;
int main()
{
int A, B, a, b;
cin >> A >> B >> a >> b;
int K = ceil((A - (a - 0.000001)) / (a + a - 0.000001));
int C = ceil((B - (b - 0.000001)) / (b + b - 0.000001));
cout << K * C;
return 0;
}
K题:哥俩好数字
数字x,y 是个「哥俩好」数字,当且仅当数字x的数位和与数字y的数位和相同。需要找n 个不同的正整数,使得这n 个数字两两之间均为「哥俩好」数字且总和最小。
Input:一个整数n,1≤n≤5000
Output:最小的「哥俩好」数字总和。
例如输入2,输出为11;哥俩好数字为1和10
。。以为是对n进行分段,比如1-10,11-99这样,如果是n=5,那么就是4+13+22+31+40=110
事实上对于一个七位的数字,数位和最大为9*7=63
不妨使用for循环逐个枚举,令i从最小值1枚举至无穷大。
对于每一个数字i,计算其数位和,设为f(i);
利用数组cnt[x]记录到目前为止数位和为x的数字个数;
数组sum[x]记录到目前为止数位和为x的数字之和(因为是从小到大顺序枚举的,所以一定是最小和)
经过枚举可以发现,在n最大为5000时,答案的解也在600000范围内,即for循环中i只需要从1枚举到600000即可,并且数位和也很小,不会超过50。
解题思路:
在枚举的过程中更新cnt[x]数组和sum[x]数组
在某个cnt[x]的值刚好到n时,用当前的sum[x]去更新答案,循环结束输出答案即可。
需要注意的是:本题sum[x]和答案会超过c++的int数据范围,故需要开long long。
#include <iostream>
#include<algorithm>
using namespace std;
int f(int x);
int n;//哥俩好数字的个数
int cnt[500];//数组cnt[x]记录到目前为止数位和为x的数字个数
long long sum[500];//数位和相同的数字之和
long long ans = 1e18;//停止值
int x = 0;//存储某一个数字的数位和
int main()
{
cin >> n;
for(int i = 1; i <= 6000000; i++)
{
x = f(i);
if (cnt[x] < n)
{
cnt[x]++;
sum[x] += i;
}
if(cnt[x] == n)
{
ans = min(ans, sum[x]);
cout << ans;
break;
}
}
}
//输入:数字
//输出:数位和
int f(int x)
{
int tot = 0;//数位和
while (x) {
tot += x % 10;//个位
x /= 10;
}
return tot;
}
D题:素数区间输出
有一个有序的素数集合。每一个集合内的元素按升序排序。集合顺序优先按「集合元素和」排序,其次按「集合字典序」排序。需要给出给定区间 [l,r]的内容。该集合的一部分为
[2], [3], [2, 3], [5], [2, 5], [7], [3, 5], [2, 7], [2, 3, 5], [3, 7], [11], [2, 3, 7], [5, 7], [2, 11], [13], [2, 5, 7],
Input:两个整数 l,r,表示给定的区间;(1≤l≤r≤1e18,r−l≤100000)
Output:这个区间[l,r]的内容。
思路:枚举总和,然后由于总和相同按字典序排列,因此可以从小往大搜索,记录当前前缀对应的数位和,如果中间有不需要输出的可以跳过。
#include <iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
#define ll long long
#define P pair<ll,ll>
using namespace std;
const int maxn = 1e6;
int isprime[maxn], p[maxn];
vector<int>primes;
vector<int>vec;
map<P, P>M;
ll a, b, cur, prefix_len;
int getL(int x)
{
int res = 0;
while (x > 0) { res++; x /= 10; }
return res + 2;
}
inline P get(P p1, P p2, ll len) {
return P(p1.first + p2.first, p1.second + p2.second + len * p1.first);
}
P calc(int x, int sum)
{
P p(x, sum);
if (sum < 0) return P(0, 0);
if (M.count(p)) return M[p];
if (sum == 0) return M[p] = P(1, 2); //ct,len
if (primes[x] > sum) return P(0, 0);
return M[p] = get(calc(x + 1, sum - primes[x]), calc(x + 1, sum), getL(primes[x]));
}
void ptchar(char c) {
cur++; if (cur >= a && cur <= b) putchar(c);
}
void print(int x) {
vector<int>v;
while (x > 0) { v.push_back(x % 10); x /= 10; }
for (int i = v.size() - 1; i >= 0; i--) ptchar(char(v[i] + '0'));
}
void print(int x, int sum)
{
if (sum < 0 || cur >= b) return;
if (sum == 0) {
ptchar('[');
for (int i = 0; i < vec.size(); i++) {
print(vec[i]);
if (i == vec.size() - 1) ptchar(']');
ptchar(',');
ptchar(' ');
} return;
}
if (primes[x] > sum) return;
vec.push_back(primes[x]);
prefix_len += getL(primes[x]);
ll len = prefix_len * calc(x + 1, sum - primes[x]).first + calc(x + 1, sum - primes[x]).second;
if (len + cur >= a) print(x + 1, sum - primes[x]);
else cur += len;
vec.pop_back();
prefix_len -= getL(primes[x]);
len = prefix_len * calc(x + 1, sum).first + calc(x + 1, sum).second;
if (len + cur >= a) print(x + 1, sum);
else cur += len;
}
int main()
{
//isprime[1] = false;
fill(isprime, isprime + maxn, true);//fill(vec.begin(), vec.end(), val); val为将要替换的值;
for (int i = 2; i < maxn; i++) //获得素数数组primes
{
if (isprime[i])
{
primes.push_back(i);//将素数放入primes数组中
for (int j = i + i; j < maxn; j += i) isprime[j] = false;//排除掉不是素数的数字
}
}
cin >> a >> b;//区间
for (int i = 2; i < 100000 && cur < b; i++) //试探过,i最大到2096,这个范围的素数也就300来个,所以记忆化
{
ll len = calc(0, i).second;
if (cur + len >= a) print(0, i);
else cur += len;
}
puts("");
return 0;
}