2019年天梯赛模拟题集-赛后总结
L1-1 帅到没朋友 (20 分)
当芸芸众生忙着在朋友圈中发照片的时候,总有一些人因为太帅而没有朋友。本题就要求你找出那些帅到没有朋友的人。
输入格式:
输入第一行给出一个正整数N
(≤100),是已知朋友圈的个数;随后N
行,每行首先给出一个正整数K
(≤1000),为朋友圈中的人数,然后列出一个朋友圈内的所有人——为方便起见,每人对应一个ID号,为5位数字(从00000到99999),ID间以空格分隔;之后给出一个正整数M
(≤10000),为待查询的人数;随后一行中列出M
个待查询的ID,以空格分隔。
注意:没有朋友的人可以是根本没安装“朋友圈”,也可以是只有自己一个人在朋友圈的人。虽然有个别自恋狂会自己把自己反复加进朋友圈,但题目保证所有K
超过1的朋友圈里都至少有2个不同的人。
输出格式:
按输入的顺序输出那些帅到没朋友的人。ID间用1个空格分隔,行的首尾不得有多余空格。如果没有人太帅,则输出No one is handsome
。
注意:同一个人可以被查询多次,但只输出一次。
输入样例1:
3
3 11111 22222 55555
2 33333 44444
4 55555 66666 99999 77777
8
55555 44444 10000 88888 22222 11111 23333 88888
输出样例1:
10000 88888 23333
输入样例2:
3
3 11111 22222 55555
2 33333 44444
4 55555 66666 99999 77777
4
55555 44444 22222 11111
输出样例2:
No one is handsome
思路:
本题我做的时候难点在于,我不知道怎么存入他们的编号,以及就算存入了,也不知道这个编号以前是否出现过。其实我们把编号当成数组的下标即可,先整体对整个数组初始化为0,每读入一个编号,让读到的该编号下标的数组值赋值为1,最后在查询时,如果该人编号下标的数组为1,就证明他有朋友,而为0的,就输出,同时因为同一个人可以被查询多次,所以在每次查询完一个没朋友的,就把它的值变为1,后面就不会被多次查询了,同时用一个变量cnt记录一共查询了几个没朋友的,如果是0个,则输出No one is handsome
#include <bits/stdc++.h>
using namespace std;
int main()
{
char a[100000];
memset(a, 0, 100000 * sizeof(char));
int n, k, m;
long number;//每个人的记录编号
cin >> n;
for(int i = 1; i <= n; i++)
{
scanf("%d",&k);
for(int j = 1; j <= k; j++)
{
scanf("%ld", &number);
if(k != 1)
{
a[number] = 1;
}
}
}
scanf("%d",&m);
int cnt = 0;//查到没朋友的人的个数
for(int i = 1; i <= m; i++)
{
scanf("%ld",&number);
if(!a[number])
{
if(!cnt)
{
printf("%05ld",number);
}
else
{
printf(" %05ld",number);
}
a[number] = 1;
cnt++;
}
}
if(cnt == 0)//没人被查到
{
printf("No one is handsome");
}
return 0;
}
L2-2 月饼
月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。
注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。
输入格式:
每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。
输出格式:
对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。
输入样例:
3 20
18 15 10
75 72 45
输出样例:
94.50
思路:
对于每一种月饼,我们用其总售价除以总库存量,可以得到该类月饼一吨的售价,我们看成单价,我们对单价进行排序,先买单价高的即可,直到卖到市场最大需求量。值得注意的是,本题宜用结构体将单种月饼的库存量和总售价以及单价一并放在一起,此时比较单价的话,就需要对sort函数传入第三个参数cmp,自定义比较的方法,这样就可以通过单价的大小,把该月饼的其他属性一并放到单价排完序的位置。
代码:
#include <bits/stdc++.h>
using namespace std;
struct node
{
double k;//总库存
double z;//总售价
double dj;//单价
}y[1005];
int n, D;//D是市场最大需求量
bool cmp(node a, node b)//重新定义sort的排序方法
{
return a.dj > b.dj;
}
int main()
{
cin >> n >> D;
for(int i = 0; i < n; i++)
{
scanf("%lf", &y[i].k);
}
for(int i = 0; i < n; i++)
{
scanf("%lf", &y[i].z);
}
for(int i = 0; i < n; i++)
{
y[i].dj = y[i].z / y[i].k;
}
sort(y, y + n, cmp);
double cnt = 0;
for(int i = 0; i < n; i++)
{
if(y[i].k >= D)
{
cnt += y[i].dj * D;
break;
}
else
{
cnt += y[i].z;
D -= y[i].k;
}
}
printf("%.2lf",cnt);
return 0;
}
蓝桥杯训练-3.25
算法学习
• 快速幂算法
先来看一个简单的例子
3^10=3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,我们将指数尽量变小,则变为
3^10=(3 * 3 )(3 * 3)(3 * 3)(3 * 3)(3 * 3)
310=(3*3)5
310=95
此时指数由10缩减成一半变成了5,而底数变成了原来的平方。
现在我们的问题是如何把指数5变成原来的一半,因为5是奇数,但我们可以这样表示
95=(94)*(9^1)
此时我们抽出了一个底数的一次方,这里即为91,这个91我们先单独移出来,剩下的就可以继续执行上面的"缩指数"操作:
95=(812)*(9^1)
继续把指数缩小一半:
95=(65611)*(9^1)
我们再把6561^1单独移出来,但是此时指数变成了0,也就说明我们无法再进行缩的操作了。
我们能够发现,最后的结果是9 * 6561,而9是当指数为奇数5的时候产生的,6561是当指数为奇数1时产生的,所以我们可以总结:
最后求出的幂结果实际上就是在变化过程中所有当指数为奇数时底数的乘积。
代码实现如下:
long long fastPower(long long base, long long power)//base表示底数,power表示指数
{
long long result = 1;
while (power > 0)
{
if(power % 2 == 0) // 如果指数是偶数的话
{
power /= 2;//把指数缩小一半
base = base * base % 1000;//底数变为原来的平方,对1000取余是因为本代码用于输出最后结果的后三位数字
}
else //如果指数为奇数
{
power = power - 1;//把指数减去1,使之变为偶数
result = result * base % 1000;//此时把指数为奇数时分离出来的一次方乘到结果里面去
power = power / 2;//此时指数为偶数,可以继续执行操作
base = base * base % 1000;
}
}
return result;
}
优化:
因为power是一个整数,当power是奇数5时,power-1=4,power/2=2;而如果我们直接用power/2=5/2=2,结果是一样的
long long fastPower(long long base, long long power) {
long long result = 1;
while (power > 0) {
if (power % 2 == 1) { //是奇数的话,就多一步把 分离出来的一次方乘起来
result = result * base % 1000;
}
power = power / 2;//偶数的话直接这样执行
base = (base * base) % 1000;
}
return result;
}
再优化:
power & 1,如果power是偶数,则其二进制表示的最后一位一定是0;如果是奇数,那么一定是1,将他们分别与1的二进制做"与"运算,得到的就算power二进制最后一位数字,是0就是偶数,是1就是奇数。
同样,对于power = power / 2来说,也可以用更快的位运算代替,把power的二进制表示向右移动1位就能变成原来的一半了。
6 : 0 0 0 0 0 1 1 0
3 : 0 0 0 0 0 0 1 1
再优化为:
long long fastPower(long long base, long long power) {
long long result = 1;
while (power > 0) {
if (power & 1) {//此处等价于if(power%2==1)
result = result * base % 1000;
}
power >>= 1;//此处等价于power=power/2
base = (base * base) % 1000;
}
return result;
}
• 高精度乘法
#include <bits/stdc++.h>
using namespace std;
string s1, s2;//输入两个字符串类型的大数据
int a[100], b[100], c[100];//a b 用来存入字符串转为的数字,c用来存放最后乘出来的答案
int main()
{
cin >> s1 >> s2;
int len1 = s1.length();
int len2 = s2.length();
for(int i = 0; i < len1; i++)
{
a[i] = s1[len1 - i - 1] - '0';//将字符串最后一位,也就是低位存入数组开头
}
for(int i = 0; i < len2; i++)
{
b[i] = s2[len2 - i - 1] - '0';
}
for(int i = 0; i < len1; i++)
{
int carry = 0;//定义的进位,没一轮乘完后,都要初始化为0
for(int j = 0 ; j < len2; j++)
{
c[i + j] += a[i] * b[j] + carry;
carry = c[i + j] / 10;
c[i + j] %= 10;
}
c[i + len2] += carry;//每一轮乘完了,如果仍有进位,而数组没有到那,需要将该进位加到下一个
}
int index = len1 + len2;//删除前导0
while(c[index] == 0 && index > 0)
index--;
for(int i = index; i >= 0; i--)
{
printf("%d",c[i]);
}
return 0;
}
代码练习
• 麦森数-(高精度-求位数)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fabwS4B8-1616686519861)(C:\Users\费泽涛\Desktop\图片\屏幕截图 2021-03-22 185723.png)]
思路:
① 首先是位数,因为2的次幂末尾没有以0结尾的数,所以2p和2p - 1有相同的位数,算2^p - 1即算2p的位数,2p 仍然还是不好算,
我们想到,当底数为10的时候,可以轻松的出10^p的位数,即p + 1,我们只要想办法把 中的底数2改为10,指数加一就是位数了。由此想到用10的几次方来代替2,那么就不难想到
,这样便可以把
中的2代换掉,变为
。根据乘方的原理,将p乘进去,原式便可化为我们最终想要的形式
了,所以位数就是
。
② 求2^p - 1的最后500位数字,可运用快速幂(下面)
代码:
#include <bits/stdc++.h>
using namespace std;
int p;//指数
int base[1001], res[1001], sav[1001];
void result_1()//因为到后面(中途),底数和结果都会变得非常大,只能用高精度乘法,而初始的时候,底数就是2,而结果是1
{
memset(sav, 0, sizeof(sav));//每次一开始都要给工具人 sav初始化为0
for(int i = 1; i <= 500; i++)//因为只需要后面500位
{
for(int j = 1; j <= 500; j++)
{
sav[i + j - 1] += res[i] * base[j];//这里先把所有乘起来,加起来的值存在本位,后面在统一进位
}
}
for(int i = 1; i <= 500; i++)//统一处理进位,这里不用管500位进到501位,因为只要500位
{
sav[i + 1] += sav[i] / 10;
sav[i] %= 10;
}
memcpy(res, sav, sizeof(res));//将所得的第一次结果全部赋值给res,因为res才是最后存的结果,sav只是中间人
}
void result_2()
{
memset(sav, 0, sizeof(sav));
for(int i = 1; i <= 500; i++)
{
for(int j = 1; j <= 500; j++)
{
sav[i + j - 1] += base[i] * base[j];
}
}
for(int i = 1; i <= 500; i++)
{
sav[i + 1] += sav[i] / 10;
sav[i] %= 10;
}
memcpy(base, sav, sizeof(base));
}
int main()
{
cin >> p;
printf("%d\n",(int)(log10(2) * p + 1));
res[1] = 1;//开始时,结果应为1,因为是后面是几次方,因为开始乘的时候,初始化应该为1,不然乘什么结果都变了
base[1] = 2;//高精度初赋值,底数为2
while(p != 0)//快速幂
{
if(p & 1) result_1();
p >>= 1;
result_2();
}
res[1] -= 1;//首位存的数结果应该减去1,因为初始化的时候给了一个1
for(int i = 500; i >= 1; i--)
{
if(i != 500 && i % 50 == 0)
printf("\n%d",res[i]);//输出格式每50个换一行
else
printf("%d",res[i]);
}
return 0;
}