第四次线上赛小结

一、没出现的数字

描述

判断一个正整数的每一位数字中是否出现了0~9这10个数字,统计有多少个数字没出现过。例如12345678,没出现过的数字是0和9,故共有2个数字没出现过。

输入

多组案例。一个正整数n,表示案例的数量。(n<=100)

每组案例由一个正整数m组成。(m<=1e+9)

输出

针对每组案例,输出一个整数,表示m中有多少个数字没出现过。

每组案例输出完要换行。

样例输入

2
10
12345678
 

样例输出

8

2

关键代码

*****
while (x)
{
	a[x % 10]++;
	x /= 10;
}
int cnt = 0;
for (int i = 0; i < 10; i++)
{
	if (a[i] == 0)
	{
		cnt++;
	}
}
*****

解释

典型的运用数组标记变量的题目,首先建立一个数组来标记每位数的数字,第一个循环是储存每位数的数字,出现了就+1,下面的循环用来遍历这十个数字,如果等于0,说明这个数字在输入的数字种没有出现过,计数加一。

二、喜欢的数字

描述

Tql学姐喜欢的数字包括2的整数次幂,以及2的整数次幂减1。每当提起某个正整数,Tql学姐总是说成与该数字最邻近的喜欢的数字。

现在已知某个正整数m,问Tql学姐会把该整数说成几?

输入

多组案例。一个正整数n,表示案例的数量。(n<=100)

每组案例由一个正整数m组成。(m<=100000)

输出

针对每组案例,输出一个整数,表示最接近m的喜欢的数字。

每组案例输出后要换行。

样例输入

3
7
13
9
 

样例输出

7

15

8

关键代码

*****
for (int i = 0; i < 30; i++)
{
	a[x++] = p;
	a[x++] = p - 1;
	p *= 2;
}
*****
for (int i = 0; i < x; i++)
{
	if (abs(a[i] - m) < min)
	{
		min = abs(a[i] - m);
		ans = a[i];
	}
}
*****

解释

先是将2的整数次幂以及二的整数次减一存入数组,接着遍历数组,寻找与输入的数最接近的数。

三、OJ新人

描述

有一位OJ新人在一次OJ线上赛中,做出了所有6道题,并且都是一遍通过。其中,第1题是比赛开始后a1分钟做出,第2题是比赛开始后a2分钟做出,...,第6题是比赛开始后a6分钟做出。例如a1~a6分别为10、15、30、80、50、150,则我们认为第1题做了10分钟,第2题做了5分钟,第3题做了15分钟,然后先用了20分钟做了第5题,再用30分钟做了第4题,用70分钟做了第6题,所有6题总共花了150分钟完成。

OJ计算小分的方法是:先计算每道做出的题按照比赛开始到该题做出的时间,然后把所有题的该时间累加。小分越小越好。由于都是一次通过,所以不用考虑罚时。故上述案例中,小分是10+15+30+80+50+150=335。

Tql学姐认为,该新人有更好的完成顺序,使得在每道题花费的时间不变的前提下,小分更小。

问最优的做题顺序下的小分比当前顺序下的小分,能少多少?

输入

多组案例。一个正整数n,表示案例的数量。(n<=100)

每组案例由6个整数组成,表示每道题是在比赛开始后多少分钟做出的。(均不大于10000)

输出

针对每组案例,输出一个整数,表示最优的做题顺序下的小分比当前顺序下的小分少多少。

每组案例输出完都要换行。

样例输入

1
10 15 30 80 50 150
 

样例输出

5

关键代码

*****
for (int i = 0; i < 6; i++)
{
	cin >> a[i];
	sum += a[i];
}
sort(a, a + 6);
int b[6];
b[0] = a[0];
for (int i = 1; i < 6; i++)
{
	b[i] = a[i] - a[i - 1];
}
sort(b, b + 6);
for (int i = 1; i < 6; i++)
{
	b[i] += b[i - 1];
}
int sum1 = 0;
for (int i = 0; i < 6; i++)
{
	sum1 += b[i];
}
***

解释

通过简单的计算,假如一个人写三道题用时第一题:1、第二题:2、第三题:3,如果他按一二三的顺序写就是10,按一三二的顺序写就是11,按二一三的顺序写就是11,按二三一的顺序写就是13,按三一二的顺序写就是13,按三二一的顺序写就是14,由此可见按时间的长短由短到长排得到的分数就是最少的。所以首先对输入的a数组进行排序,用b数组储存每道题所用的时间,再对b数组进行排序,然后依照排序好的b数组进行计算分数即可。

四、斐波那契汤

描述

食堂的大师傅认为,上好的汤是采日月之精华、吸天地之灵气熬制而成,于是今天的汤等于昨天的汤加前天的汤。

设今天的汤、昨天的汤、前天的汤的营养价值分别为v3、v2、v1,满足公式v3=v2/2+v1/3,其中除法都是只保留整数部分的除法。该公式对于除了头两天外的任何一天都成立,头两天的营养价值a和b是给定的。

由于营养价值会衰减,大师傅会每逢整5的日子(第5、10、15、...天)向汤里额外加入营养价值为c的神秘物质,以增加该天汤里的营养价值。

DJ杨老师第m天去食堂买了一份汤,问该汤的营养价值是多少?

输入

多组案例。一个正整数n,表示案例的数量。(n<=20)

每组案例由4个正整数a、b、c、m组成,分别表示第一天的营养价值、第二天的营养价值、额外加入的营养价值、天数。(a<=10000, b<=10000, c<=10000, m=1000000)

输出

针对每组案例,输出一个整数,表示第m天汤的营养价值。这个数字不会超过100000。

每组案例输出完都要换行。

样例输入

2
10 20 10 5
10000 10000 10000 100
 

样例输出

20

17701

关键代码

*****
if (m == 1)
{
	cout << a << endl;
}
else if (m == 2)
{
	cout << b << endl;
}
else
{
	for (int i = 3; i <= m; i++)
	{
		ans = b / 2 + a / 3;
		if (i % 5 == 0)
		{
			ans += c;
		}
		a = b, b = ans;
	}
	cout << ans << endl;
}
*****

解释

典型的斐波那契的数列的变式,由题意可知,第一天是a,第二天是b,接下去的营养都是昨天/2+前天/3,逢5的倍数加c,最后用b的营养是前天的,用a储存,ans里面的营养是昨天的,用b储存(顺序不能交换)。

五、公共质因数的和

描述

有两个正整数a和b,如果质数c既是a的因子,也是b的因子,则称c是a和b的公共质因数。

求a和b的所有公共质因数之和。

输入

多组案例。一个正整数n,表示案例的数量。(n<=100)

每组案例由两个正整数a和b组成。(a<=1e+8,b<=1e+8)

输出

针对每组案例,输出一个整数,表示a和b的所有公共质因数之和。(不会超出int范围)

每组案例输出完要换行。

样例输入

2


10 15
36 48
 

样例输出

5

5

关键代码

*****
int gcd(int a, int b)//最大公约数(辗转相除法)
{
	return !b ? a : gcd(b, a % b);
}
bool isPrime(int a)//素数判断
{
	if (a < 2)
	{
		return false;
	}
	for (int i = 2; i <= sqrt(a); i++)
	{
		if (a % i == 0)
		{
			return false;
		}
	}
	return true;
}
*****
int c = gcd(a, b);
int sum = 0;
for (int i = 2; i <= c; i++)
{
	if (a % i == 0 && b % i == 0)
	{
		if (isPrime(i))
		{
			sum += i;
		}
	}
}
*****
//另一种方法,分解质因数
*****
for (int i = 2; i * i <= a; i++)
{
	if (a % i == 0)
	{
		if (b % i == 0)
		{
			ans += i;
		}
		while (a % i == 0)
		{
			a /= i;
		}
	}
}
if (a > 1)
{
	if (b % a == 0)
	{
		ans += a;
	}
}
*****

解释

首先是熟读并背诵的两个函数:求最大公约数以及判断素数。,接着从2遍历到他们的最大公约数,如果a,b都能整除这个数就判断是不是素数,是素数就加上去。另一种方法是分解质因数,由于一个数除去一以外最小的因数一定是质数,例如36的最小因数是2,当36完全除去2之后就是9,9的最小因数是3,完全除去后还是3,所以应用这个性质就能找出a与b的公共质因数,最后有个特殊的是a除到最后变成质数,所以就要特殊判断b%a是否也等于0。

六、最大乘积

描述

把一个正偶数m拆分成两个质数之和(可以是相同的质数),输出这两个质数可能的最大乘积。

输入

多组案例。一个正整数n,表示案例的数量。(n<=100)

每组案例由一个正偶数m组成。(4<=m<=1000000)

输出

针对每组案例,输出一个长整数,表示依据【问题描述】算出来的最大乘积。

每组案例输出完要换行。

样例输入

2
10
20
 

样例输出

25

91

关键代码

*****
typedef long long ll;
const ll maxn = 1000007;
bool a[maxn];
void findPrime()
{
	for (int i = 2; i < maxn; i++)
	{
		a[i] = 1;
	}
	for (int i = 2; i * i < maxn; i++)
	{
		if (a[i])
		{
			for (int j = i * i; j < maxn; j += i)
			{
				a[j] = 0;
			}
		}
	}
}
*****
for (ll i = x / 2; i >= 2; i--)
{
	if (a[i] && a[x - i])
	{
		ans = i * (x - i);
		break;
	}
}
*****

解释

用传统的判断素数的方法肯定超时,数据量很大,所以就要用到埃氏筛的方法,判断一个数是不是素数,如果是的话就把它的倍数全筛走,比如2是素数,就将4、6、8、10、12……筛走因为他们必有一个因数2,3是素数,就将6、9、12、15、18……筛走(下面有动图)。findPrime()函数就是实现了这个效果,先将数组的所有值都认定为素数,然后2是素数,将2的倍数全筛了,3是素数,将3的倍数全筛了,4因为被2晒过,a[4]=0了,不用筛,5是素数……以此类推。接下去就是越靠近输入的数的一半乘积越大,所以我们直接从一半开始往二遍历一找到符合要求的就退出循环就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值