一次编程竞赛

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淮杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值