CSDN竞赛13期题解

总结

这次竞赛放在工作日的晚上,下班回来挺困了,做了两题都快睡着了,状态不佳,做的不是很理想。

总的来说,这次竞赛的 T 2 T2 T2考察贪心的题目还是挺不错的,其他题目更多的是考察阅读理解。

题目列表

1.陶陶摘苹果

题目描述

陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 10 个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个 30 厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。 现在已知 10 个苹果到地面的高度,以及陶 陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。

分析

签到题,能够触及的最大高度是 m + 30 m+30 m+30,遍历下苹果树的高度,不超过最大高度,都可以摘到。

代码

#include <iostream>
#include <string>
int solution(int arr[10], int m) {
	int t = m + 30;
	int res = 0;
	for(int i = 0; i < 10; i++) {
		if(arr[i] <= t) res++;
	}
	return res;
}
int main() {
	int arr[10];
	int max_tourch_height;
	for (int i = 0; i < 10; i++) {
		std::cin>>arr[i];
	}
	std::cin>>max_tourch_height;
	int result = solution(arr, max_tourch_height);
	std::cout<<result<<std::endl;
	return 0;
}

2.硬币的面值

题目描述

小A有n枚硬币,现在要买一样不超过m元的商品,他不想被找零,同时又不想带太多的硬币,且硬币可以重复,现在已知这n枚硬币的价值,请问最少需要多少硬币就能组合成所有可能的价格?

输入描述:

第一行两个数:n、m。
下一行,共n个数字,表示硬币的面值。

输出描述:

一行一个数,表示最少需要多少硬币。如果无解请输出“No answer!!!”

输入样例:

5 31
1 2 8 4 16

输出样例:

5

分析

本题考察贪心,如果没有做过类似的题目,现场想出来还是挺不容易的。要组成1到m之间所有可能的价格,最基本的条件就是带上1元硬币,只有这样才能购买1元的商品而不被找零。如果小A有足够多的1元硬币,也是可以购买任意价格的商品的,所以有没有1元硬币就是判断问题是否有解的依据。

举个例子:用1 2 5 10四种面值的硬币组成1到20种所有的价格,要求携带的硬币数最少。

  • 1是必须携带的,为了购买1元的商品。
  • 如果要购买1到2元的商品,可以选择带2个1,也可以选择带1个1和1个2,我们选择后者,因为带2个1只能组成1到2的价格,而带上1个1和1个2可以组成1到3的价格,我们希望组成的价格范围越大越好,所以选择带上2。
  • 现在1到3的价格都可以组成了,为了组成4,我们只能再带一个1和一个2,还是贪心的选择了2,这样就可以组成1到5之间所有的价格了。
  • 再带上一个5,我们就能组成1到10之间所有的价格。
  • 最后带上10,就可以组成1到20之间所有的价格。

经过上面的分析,我们发现只需要携带5枚硬币就可以组成1到20之间所有的价格了。

对于一般的情况,使用 1 , a 2 , a 3 , . . . , a n 1,a_2,a_3,...,a_n 1,a2,a3,...,an来组成1到m的价格。假设我们使用前 i − 1 i-1 i1个硬币组成了1到S之间所有的价格。

  • 如果 a i − 1 > S a_i - 1> S ai1>S,那么需要更多的硬币才能表示前 a i − 1 ai -1 ai1的价格,我们选择不断的加上 a i − 1 a_{i-1} ai1,直至S不小于 a i − 1 ai -1 ai1
  • 如果 a i − 1 < = S a_i - 1<= S ai1<=S,那么可以先继续考虑下一个更大面值的硬币了

为什么以 a i − 1 a_i -1 ai1为分界点呢?因为只有S不小于 a i − 1 a_i-1 ai1,再使用 a i a_i ai才能有效的扩展S。比如S = 3,直接加上5面值的硬币,那么4就不能表示出来了。所以本题的贪心策略也就是:自小到大排序硬币的面值,去掉超过m的面值,因为用不上。然后遍历面值数组,遍历到 a i a_i ai时,首先判断 a i − 1 a_i-1 ai1 S S S的大小关系,如果S较小,就不断的对S累加上 a i − 1 a_{i-1} ai1,直至S不小于 a i − 1 ai-1 ai1

由于遍历的过程就是不断的把 S S S a i − 1 a_i-1 ai1上累加的过程,所以可以设置最后一个硬币为哨兵节点,面值为m + 1,这样遍历到最后一个硬币面值, S S S的值就可以保证不小于 m m m了。

代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 105;
int a[N];
int main() {
    int m, n;
    cin>>n>>m;
    for(int i = 0; i< n;i++) cin>>a[i];
    sort(a, a + n);
    if (a[0] != 1)  cout<<"No answer!!!"<<endl;
    else {
        while(a[n - 1] >= m)    n--;
        a[n] = m + 1;
        int s = 0, res = 0;
        for(int i = 1;i <= n;i++) {
            if(s >= m)  break;
            if (s < a[i] - 1) {
                int k = (a[i] - s + a[i-1] - 2) / a[i-1];
                res += k;
                s += k * a[i - 1];
            }
        }
        cout<<res<<endl;
    }
    return 0;
}

3. 公司新表

题目描述

公司里为了凸显公司的特性。
安装了一个n进制表。
已知新的表的时间是”H:M”。
时间合法的定义为H<=23 && M<=59。
时间有多少种进制定义的方式,依次打印出来。
如果有无数种解输出”-1”,不存在输出”0”。

输入描述:

输入一行字符串a:b形式。

输出描述:

输出答案。

输入样例:

11:20

输出样例:

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

分析

这题题目描述不够清楚,但是有用例解释还是可以理解的,给定一个字符串,如果用k进制解释这个字符串后的数字大小在时钟范围内,就是合法的。简单的模拟题,由于比赛时状态不佳,只通过了六成用例,赛后重新写了下。

首先要考虑的是字符串的解析,尽管题目用例给的是像 11 : 20 11:20 11:20之类的字符串,但是是否存在 1 : 2 1:2 1:2,甚至是 122 : 234 122:234 122:234类似的字符串,也不能保证。字符串中出现的最大字符转化为数字后是t,那么至少要t + 1进制才能表示该字符串。

如果字符串是用 i i i进制表示的,将冒号前后的字符串转化为十进制数字后得到 x : y x:y x:y,只要 x < 24 , y < 60 x<24,y<60 x<24,y<60,那么这个进制表示就是合法的。注意如果使用 i i i进制表示是合法的,那么使用 i − 1 i-1 i1进制表示的数值只会更小,也是合法的。像 01 : 01 01:01 01:01这类只有一位的表示,不管是几进制表示得到的数都很小,因为最低位的基数都是1。我们可以从 t + 1 t + 1 t+1枚举到60,如果60进制都是合法的,更高进制的表示一定也是合法的,因为如果时间有两位表示,60进制的两位数是不小于60的。60进制合法就表示有无数种解。

代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int get(char c) {
	if (c >= '0' && c <= '9')	return c - '0';
	else	return c - 'A' + 10;
}
std::vector<int> solution(std::string m) {
	std::vector<int> res;
	int t = 0;
	int n = m.size();
	for(int i = 0; i < n; i++) {
		if(m[i] == ':') continue;
		t = max(t, get(m[i]));
	}
	for(int i = t + 1; i <= 60; i++) {
		int b[2] = {0, 0};
		int p = 0;
		for(int j = 0;j < n;j++) {
			if(m[j] == ':') {
				p++;
				continue;
			}
			int q = get(m[j]);
			b[p] = b[p] * i + q;
		}
		if(b[0] >= 24 || b[1] >= 60) break;
		else res.push_back(i);
	}
	if (!res.size()) {
		return {0};
	} else if(res.back() == 60) return {-1};
	return res;
}
int main() {
	std::string m;
	getline(std::cin, m);;
	std::vector<int> result = solution(m);
	for(auto it=result.begin(); it!=result.end(); ++it) {
		std::cout<<*it<<" ";
	}
	std::cout<<std::endl;
	return 0;
}

4.题目名称:小豚鼠排排坐

题目描述

小艺酱买了一个由一排排格子组成的小房子n*m,她想让k个小豚鼠每个小豚鼠都有自己的格子。但是为了不浪费空间,她想要最边角的一圈2*(n+m-2)每行每列的格子都有一个小豚鼠居住。
具体来说,假设这k只小豚鼠的格子坐标为(x1, y1), (x2, y2),…,(xk, yk),则需要满足存在1<a,b,c,d<=k,使得xa =1, xb = n, yc = 1, yd = m (a, b, c, d可以重复).
小艺酱想知道自己有多少种方案安排小豚鼠。

输入描述:

输入整数n,m,k。(1<=n,m<=5,0<=k<=n*m)

输出描述:

输出方案数。

输入样例:

3 3 2

输出样例:

2

分析

本题给的数据范围很小,所以可以直接暴搜,甚至不用优化。要注意的是小豚鼠可以住在中间的格子,不是非要住在边界格子。而且并不是每行每列都需要有小豚鼠居住,只要最外围的行和列至少住了一只就可以了。dfs时可以使用状态压缩表示和判断状态,具体实现见代码。

代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int a[30],b[30];
int ans = 0,cnt = 0;
int n,m,k;
void dfs(int u,int c,int s,int t) {
	if(c == k) {
		if((s & 1) && (t & 1)) {
			if((s >> (n - 1) & 1) && (t >> (m-1) & 1)) ans++;
			return;
		}
	}
	if(u >= cnt) return;
    //放小豚鼠
	dfs(u+1,c+1,s | (1 << a[u]),t | (1 << b[u]));
    //不放小豚鼠
	dfs(u+1,c,s,t);
}
int main() {
	cin>>n>>m>>k;
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < m; j++) {
			a[cnt] = i;
			b[cnt++] = j;
		}
	}
	dfs(0, 0, 0 , 0);
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值