【题解专栏】南华大学19级软卓选拔赛题解

在我的题解里,我会把所有题目的代码进行力所能及的逐句分析

(4.21晚有修改,更新了c题题解)

A 涛哥补足精神

题面

题目描述

涛哥为了给你们出这道题可是费尽了心思啊,于是涛哥决定去睡觉a分钟,只有总共睡足了a分钟涛哥才会起床,并且涛哥每次躺在床上要过d分钟才能睡着。于是涛哥先定了一个闹钟,闹钟会在b分钟后响起,在那之后,如果每次闹钟响起来后如果涛哥还没睡够,他会把闹钟调整到再过c分钟后响起,接着继续睡。如果闹钟响起时涛哥睡着的总时间已经不小于a分钟,涛哥才会起床。否则涛哥将永远不会起床。

请你写一个程序判断涛哥能不能起床,如果可以,求出总共过了多久才会起床。

输入
第一行输入整数t代表t次询问
之后每次询问都要输入四个数字a b c d,意义如上

输出
每次询问输出一行
如果不能起床就输出-1

否则输出起床所需时间

样例输入 
2
10 5 5 1
10 5 1 2

样例输出 Copy

15
-1

提示
有10%测试样例的数据范围在long long类型中

这题没有考什么算法,就是一道考基础的模拟题,但是题意的确有些绕。写出这题还是需要比较扎实的c++基本功的

#include<iostream>
#define ll long long
using namespace std;

int main()
{
	ll t = 0;//根据题意,有10%的数据在longlong类,因此答案需要用longlong
	cin >> t;//t次询问
	while (t--)
	{
		ll a, b, c, d,ans=0;
		cin >> a >> b >> c >> d;//定义与输入
		if (a <= b)//如果需要的睡眠小于第一次闹钟的响铃时间便在第一次闹钟响起后醒过来
		{
			cout << b << endl;//输出答案
			continue;//进入下一次循环
		}
		else
		{
			if (c <= d)//如果再一次入睡时间要大于响铃时间则再也起不了床
			{
				cout << "-1" << endl;//按照题意输出-1
				continue;
			}
			else//这是能起床的情况
			{
				ans = b;//先把答案初始化为第一次闹铃响起的时间
				a -= b;//更新还需要睡眠的时间
				ll k = c - d;//k代表入睡时间,即闹铃间距减去入睡所需时间
				if (a % k == 0)//算算闹铃要响几次才能起床
				{
					ans += c * (a / k);//答案加几次时间
				}
				else//这里要注意,即使睡眠时间够了,但是闹铃不响是不会起来的,所以需要(a/k+1)
				{
					ans += c * ((a / k) + 1);//答案更新
				}
				cout << ans << endl;//输出答案
			}
		}
	}
	return 0;
}


软卓选拔B 后缀零计数

题目描述

输入一个长度为n的数组,要求你算出把这 n 个数乘起来后的数字有多少个后缀0

输入
第一行输入正整数 n 代表数组长度
第二行输入 n 个正整数 a1、a2、...、an

 
输出
输出为一个整数,后缀0个数
样例输入 Copy

5
1 2 3 4 5

样例输出 
1

提示
1x2x3x4x5=120,只有一个后缀0

0 < n <= 100000

0 < ai  <= 1e9

这是一道数学题目,可能有很多同学会把这个数算出来后再看看有几个后缀零,但是有一点需要注意的是,longlong数据类型的极限是10的18次方,如果把这个数完整的算出来肯定是会超过这个值的,所以我们需要用另外的方法来解决这个问题,当然,直接算是可以拿一部分分数的。真正的比赛可以这样拿一部分分数
思路:首先,出现0就肯定是相乘出现了10,只有相乘出现了10的倍数才可能有0,比如45等于20,实际上就是225=210等于20,那么我们再看看10,只有25才等于10,而103=20可以看做253,那么问题就转化成了看你的数据能被分解成多少个2和多少个5,比如10,2,8,5,一共5个2,2个5,能凑出2个2*5,那答案就是2

#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
ll x, y;//用来记录一共出现了多少个2和5
ll a[100000];//用来记录数据
void fun(ll t)//一个求解t最多能除以几次2的函数,递归实现
{
	if (t % 2 == 0)
	{
		x++;
		fun(t / 2);
	}
	else
		return;
}
void fun2(ll t)//一个求解t最多能除以几次2的函数,递归实现
{
	if (t % 5 == 0)
	{
		y++;
		fun2(t / 5);
	}
	else
		return;
}
int main()
{
	ll n;
	cin >> n;
	for (ll i = 0; i < n; i++)
	{
		cin >> a[i];
		if (a[i] % 2 == 0)
		{
			fun(a[i]);//用来算有几个2,因为x是全局变量,所以不会因为循环而每次归零
		}
		if (a[i] % 5 == 0)
		{
			fun2(a[i]);//用来算有几个5
		}
	}
	cout << min(x, y);//输出x,y中较小的那一个数就是正确答案
	return 0;
}

软卓选拔C 塔子哥算概率

题目描述
给一个自然数序列1到n,塔子哥从序列中随机抽取三个数(每个数字只能选一次)
塔子哥想问你,抽到的这三个数的和刚好是k的整数倍的概率是多少?
为了防止精度差,请输出答案的最简分式“a/b”
保证存在答案,即分子不为0

输入
第一行两个整数n,k表示自然数序列长度和题意的k

输出
输出形如“a/b”的最简分式(不含双引号)

样例输入 
4 4

样例输出 
1/4

提示
数据分布:20%的数据:3<=n<=200,1<=k<=100
40%的数据:3<=n<=2000,1<=k<=100
70%的数据:3<=n<=10000,1<=k<=200
100%的数据:3<=n<=100000,1<=k<=2000 

算法题,数学题:哈希算法,离散数学
去年最难的一道题,并且是去年没有学长写出来的一道题…笔者到现在总算写了出来…由于笔者能力有限以及这题的确存在难度,因此如果有同学实在想知道这道题请私聊笔者QQ(1536775802)笔者将亲自进行讲解。

塔子哥说:
这是一道原题改编自离散数学P250 课后习题12.1
不难发现:这是一个古典概型,分母是确定的,主要计算分子,也就是符合要求的方案数.
拿20分的解法:三重循环,暴力统计所有合法方案.复杂度O(n^3)
拿40分的解法:优化:三重循环,最内层循环根据外面两重循环的值来确定,跨步为m,复杂度为
O(\frac{n^3}{m} )
拿70分的解法:同余类的应用.
先把书上那题的解法彻底搞懂:
https://www.zybang.com/question/47028159d760daa405307dd3158dd42e.html
这样以来,我们的三重循环的上界就不再是n,而是m。根据简单的计数原理,每次统计三个同余类中数字的个数,求组合数。
内层我使用了map存储i , j , k,但是由于每次只存储三个元素,所以这部分复杂度是个常系数,可以忽略不计.
内层组合数的计算,不难发现,计算次数恒等于3,也是一个常系数,忽略不计.
所以总复杂度:O(m^3 )
拿100分的解法:
根据同余关系恒等式 . (i,j,k分别为三层循环的循环变量)
最内层循环的k可以根据外层确定的i,j O(1)的计算出来,优化掉最内层循环.
复杂度降为O(m^2) 可以完全通过此题。

官方代码

#include<bits/stdc++.h>//万能头文件
using namespace std;
const int maxn = 1e5 + 5;
#define ll long long
ll C(ll a , ll b)
{
    if (a < b) return 0;
    if (b == 0 || b == a) return 1;
    if (b > a - b) b = a - b;
    ll up = 1 , down = 1;
    for (int i = 0 ; i < b ; i++){
        up = up * (a - i);
        down = down * (b - i);
    }
    return up / down;
}
ll R[maxn];
map <ll,ll> book;
int main()
{
    ll n , m ; cin >> n >> m;
    for (int i = 1 ; i <= n ; i++)
        R[i%m]++;
    ll cnt = 0 , res = 1;
    for (int i = 0 ; i < m ; i++){
        for (int j = i ; j < m ; j++){
            ll k = (m - i - j + 3 * m)%m;
            if (k < j) continue;
            if ((i + j + k)%m == 0){
                book.clear();
                book[i]++ , book[j]++ , book[k]++;
                res = 1;
                for (auto g : book) {
                    res *= C(R[g.first] , g.second);
                }
                cnt += res;
            }
        }
    }
    ll g = __gcd(cnt , C(n , 3));
    cout << cnt / g << "/" << C(n , 3) / g << endl;
    return 0;
}

笔者的代码

#include<iostream>
#define ll long long
using namespace std;

ll gcd(ll a, ll b)//辗转相除法求最大公因数
{
	return b == 0 ? a : gcd(b,a%b);
}

ll hx[2050];//余数哈希表
ll n, k;
ll ans;

ll res(ll x, ll y, ll z)//(x+y+z)%k==0时的情况
{
	if (x == y && y == z)
	{
		return hx[x] * (hx[y] - 1) * (hx[z] - 2)/6;  //排列组合知识
	}
	else if (x == y)
	{
		return hx[x] * (hx[y] - 1) * hx[z] / 2;//排列组合知识
	}
	else if (z == y)
	{
		return hx[x] * (hx[y] - 1) * hx[z] / 2;//排列组合知识
	}
	else
	return (hx[x] * hx[y] * hx[z]);
}

int main()
{
	cin >> n >> k;
	for (ll i = 1; i <= n; i++)
	{		
		hx[i % k]++;//只存下该数据的余数
	}
	for (ll i = 0; i < k; i++)//二重循环
	{
		for (ll j = i; j < k; j++)
		{
			ll p = (k * 2 - i - j) % k;//(x+y+z)%k必须等于0
			if(p>=j)//i,j,p必须严格递增才能保证不重复
			{
				ans += res(i,j,p);
			}
		}
    }
  ll ans2 = (n) * (n - 1) * (n - 2) / 6;//排列组合C(n 3)
	ll j = gcd(ans, ans2);
	ans = ans/j;
	ans2 = ans2/j ;
	cout << ans << '/' << ans2 << endl;
	return 0;
}

软卓选拔D 小y学数学

题目描述

面临期末考试的压力,小y拾起了他许久未看的数学书,准备在数学考试之前突习一波,突然她在书上碰到了一个题目,题目的意思如下:给定你一个数n,找到一个另外一个数ans,使得ans每个数位上的乘积等于n,现在要求ans的值最小,若是不存在这个数ans,则返回-1.小y很久没有看数学了,所以她想请教博学的你,请你写个程序帮他解决这个问题请注意:至少两位数才能称作乘积,一位数无法构成乘积关系

输入
一个正整数,代表n的值。

输出
一个数,要是ans存在,ans为最小,若是不存在,输出-1,

样例输入 
10

样例输出 
25

提示
对于40%的数据,0<=n<=1e4
对于60%的数据,0<=n<=1e6
对于80%的数据,0<=n<=1e8
对于100%的数据,0<=n<=1e10

依然是思维题,想到了就不难,但就是难想到。想到了也还考验你的基本功

#include<iostream>
#define ll long long
using namespace std;

int main()
{
	ll n;
	cin >> n;
	if (n<10)//如果这个数小于10的情况,特判一下,输出这个数加10就可以了
	{
		cout << 10+n;
		return 0;
	}
	else
	{
	    /*依次把这个数分解因子,必须要从9往1去求,因为我们8=2*2*2,结果里写三个2当然不如写一个8*/
		int x9 = 0;
		while (n % 9 == 0) { x9++; n /= 9; }
		int x8 = 0;
		while (n % 8 == 0) { x8++; n /= 8; }
		int x7 = 0;
		while (n % 7 == 0) { x7++; n /= 7; }
		int x6 = 0;
		while (n % 6 == 0) { x6++; n /= 6; }
		int x5 = 0;
		while (n % 5 == 0) { x5++; n /= 5; }
		int x4 = 0;
		while (n % 4 == 0) { x4++; n /= 4; }
		int x3 = 0;
		while (n % 3 == 0) { x3++; n /= 3; }
		int x2 = 0;
		while (n % 2 == 0) { x2++; n /= 2; }
		if (n != 1)//如果这个数是大于10的质数,那么上面x1到x9全为0,否则被分解到这一步n一定等于1
		{
			cout << -1;//输出-1,无答案
			return 0;
		}

        /*之后只要按照从小到大把这些因数输出就行了,这样子输出的数各个位数相乘一定是等于原数的*/
		for (int i = 0; i < x2; i++)
		{
			cout << 2;
		}
		for (int i = 0; i < x3; i++)
		{
			cout << 3;
		}
		for (int i = 0; i < x4; i++)
		{
			cout << 4;
		}
		for (int i = 0; i < x5; i++)
		{
			cout << 5;
		}
		for (int i = 0; i < x6; i++)
		{
			cout << 6;
		}
		for (int i = 0; i < x7; i++)
		{
			cout << 7;
		}
		for (int i = 0; i < x8; i++)
		{
			cout << 8;
		}
		for (int i = 0; i < x9; i++)
		{
			cout << 9;
		}
	}
	return 0;
}

软卓选拔E 小y上楼梯

题目描述

小最近忙于练习身体,每天去健身房练习,但是她家住的楼太高,每次都要上下楼梯,下楼梯很轻松,但是每次气喘吁吁的回来时,觉得楼梯就是那一道不可逾越的天堑了,因为小y力气不够了,所以她上楼梯的时候总是根据自己当前的力气决定上楼梯的阶数的,但是她最多上k阶楼梯,最少上1阶,总共要上N阶楼梯,小y从楼底部开始上楼梯,小y想知道他有多少种到她家的方法,这样小y在上楼梯的途中也不会枯燥,毕竟可以边想问题边上楼梯,但是直到小y到了家,也没能想出这个问题,所以她请教博学的你,你能写个程序帮他解决这个问题吗?

输入
两个数N,K。

输出
一个数ans,代表上楼梯的方法数(这个数可能很大,请输出这个数对1e9+7取模的值

样例输入 
4 2

样例输出 
5

提示
样例解释:当N=4,k=2时,小y在楼底,一开始没有站上台阶,上台阶的路径可以有1->2->3->4,1->3->4,1->2->4,2->3->4,2-4,有5条路径
对于50%的数据,1<=k<=N<=30
对于70%的数据,1<=k<=N<=50
对于100%的数据,1<=k<=N<=100

本题为算法题,考的是动态规划,如果是没有学过动态规划算法的同学是不太可能写出这道题的,除非你能推导出这道题的规律,变成一道找规律的题目。然而,对动态规划比较熟练的同学来说这道题其实就很简单了。由于篇幅有限,在这里就不介绍什么是动态规划算法了。感兴趣可以在csdn上搜索学习

#include<iostream>
#define ll long long
using namespace std;
ll q = 1e9 + 7;
int main()
{
	int n, k;
	ll dp[101];
	dp[0] = 1;//上第0层楼梯的方案为1
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		dp[i] = 0;
		for (int j = 1; j <= k && i - j >= 0; j++)
		{
			dp[i] = ((dp[i] % q) + (dp[i - j] % q)) % q;
			/*转移方程,上到每i层楼梯的方案数为第i-1到第i-k层的方案数之和*/
		}
	}
	cout << dp[n];//输出到第n层的方案数
	return 0;
}

软卓选拔F 杰瑞的2020

题目描述
杰瑞最近很苦恼,他看到别人520收到了各种礼物,他却没有收到任何东西,可能是因为杰瑞的帅气,上天让他碰到了丘比特,杰瑞苦苦的哀求丘比特,给他送个女朋友,可是丘比特不会无缘无故的送女友哦,他给杰瑞出了个题目,题意如下,丘比特给杰瑞一个正整数杰瑞需要回答这个数里面有多少个2020,2020可以是不连续的,但是每个数只能用一次,不能重复使用,杰瑞因为最近苦恼,智商也降低了好多,所以他请求仗义的你,请你写个程序帮助他,帮他早日脱离苦海。

输入
一个正整数

输出
一个数,代表n中含2020的个数

样例输入 
2000020002020

样例输出 
2

提示
样例输入2
0220
样例输出2
0



设正整数的长度为k
对于20%的数据,1<=k<=10
40%的数据,1<=k<=20
100%的数据,1<=k<=1e5

非算法题,基础题,考察的是对字符串的处理和使用。考察的是代码基本功。

#include<iostream>
#include<string>
#define ll long long
using namespace std;

int main()
{
	string a;
	string b="";//定义一个空字符串
	int ans = 0;//答案
	cin >> a;//字符串输入
	for (int i = 0; i < a.size(); i++)//遍历a字符串,删除非‘2’和‘0’的字符,存入b
	{
		if (a[i] == '2' || a[i] == '0')
		{
			b += a[i];
		}
	}
	
	int t = 0;//这里的t代表寻找的是’2020‘的第几个
	for (int i = 0; i < b.size(); i++)
	{
		if (b[i] == '2' && t == 0)
		{
			b[i] = 'a';//变成a就不会再次被选了,但好像也没啥用?
			t++;
		}
		if (b[i] == '0' && t == 1)
		{
			b[i] = 'a';
			t++;
		}
		if (b[i] == '2' && t == 2)
		{
			b[i] = 'a';
			t++;
		}
		if (b[i] == '0' && t == 3)
		{
			b[i] = 'a';
			t = 0;
			ans++;
		}
	}
	cout << ans;//最后输出答案
	return 0;
}

作者:Avalon·Demerzel,希望本博客能对你有帮助,如有疑问或发现错误,欢迎下方评论。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值