CSDN竞赛第54期-个人解答

1 陶陶摘苹果

题目描述

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

AC 代码

int solution(int arr[10], int max_tourch_height) {
	int result = 0;
	for (int i = 0; i < 10; ++i) {
		if (max_tourch_height+30 >= arr[i]) {
			++result;
		}
	}
	return result;
}

2 交际圈

题目描述

小明参加了个大型聚会。聚会上有n个人参加,我们将他们编号为1…n,有些人已经互相认识了,有些人还不认识。聚会开始后,假设A跟B认识,A会给所有他认识的人介绍B,原先跟A认识,但不认识B的人,都会在此时,跟B互相认识。当所有人都把自己认识的人介绍一遍后,此时n个人就会形成k个交际圈,同一个交际圈中,两两互相认识,不同的交际圈之间,互相不认识 问题:当所有人都把自己认识的人介绍一遍后,形成了多少个交际圈?

思路

建立并查集,最后找出极大连通子图的个数。
个人对并查集的理解,可以概括为一句话:查他祖宗十八代

AC 代码

class UnionSet { // 并查集的英文好像是 `Union-Find Set` 或 `Disjoint-Set`,但是赛时没想起来这个词
public:
	UnionSet(const int n_) {
		n = n_;
		ancestor = new int[n+1];
		for (int i = n; i > 0; --i) {
			ancestor[i] = i;
		}
	}
	
	// 联通编号为 u 和 v 的节点
	void add(const int u, const int v) {
		ancestor[findAncestor(u)] = findAncestor(v);
	}
	
	int findAncestor(const int node) {
		if (ancestor[node] != node) {
			ancestor[node] = findAncestor(ancestor[node]);
		}
		return ancestor[node];
	}
	
	// 获取连通分量个数
	int numAncestors() {
		int ans = 0;
		for (int i = n; i > 0; --i) {
			if (ancestor[i] == i) {
				++ans;
			}
		}
		return ans;
	}
	
	int n;
	int* ancestor;
};

int solution(int n, int m, std::vector<std::vector<std::string>>& matrix){
	int result;
	UnionSet us(n);
	for (int i = 0; i < m; ++i) {
		const int u = atoi(matrix[i][0].data());
		const int v = atoi(matrix[i][1].data());
		us.add(u, v);
	}
	result = us.numAncestors();
	return result;
}

3 编码

题目描述

编码工作常被运用于密文或压缩传输。这里我们用一种最简单的编码方式进行编码:把一些有规律的单词编成数字。 字母表中共有26个字母{a,b,…,z},这些特殊的单词长度不超过6且字母按升序排列。把所有这样的长度相同的单词放在一起,按字典顺序排列,一个单词的编码就对应着它在整个序列中的位置。 你的任务就是对于所给的单词,求出它的编码。

思路

首先找出非法输入,非法输入是指存在后面的字符比前面的字符大的字符串。
接着用暴力,枚举编码所对应的字符串,直到找到所求字符串。
这一题的关键是找到一个字符串所对应的下一个字符串,具体算法这里不介绍,直接看代码。

AC 代码

void next(char str[]) {
	const int len = strlen(str);
	if (str[len-1] == 'z') {
		int i = len - 2;
		while (i >= 0 && str[i]+1 == str[i+1]) {
			--i;
		}
		if (i < 0) {
			for (i = 0; i <= len; ++i) {
				str[i] = i + 'a';
			}
		} else {
			++str[i];
			for (++i; i < len; ++i) {
				str[i] = str[i-1]+1;
			}
		}
	} else {
		++str[len-1];
	}
}

int solution(std::string word){
	int result = 1;
	for (int i = 1; (size_t) i < word.length(); ++i) {
		if (word[i] <= word[i-1]) {
			return 0;
		}
	}
	char str[10]{"a"};
	while (strcmp(str, word.data()) != 0) {
		next(str);
		++result;
	}
	return result;
}

4 选择客栈

题目描述

丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从 1 到 n 编号。每家客栈都按照某一种色调进行装饰(总共 k
种,用整数 0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。 两位游客一起去丽江旅
游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一
家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p 。 他们想
知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p 元的咖啡店小聚。

思路

针对每一种颜色各做一次计算:记录并维护已知的消费得起的客栈数目和消费不起的客栈数目,从前向后遍历咖啡店的最低消费,根据客栈的颜色和价格更新两个记录。

代码

int solution(int n, int k, int p, std::vector<std::vector<std::string>>& vec){
	int result = 0;
	int* colors = new int[n+1];
	int* prices = new int[n+1];
	for (int i = 0; i < n; ++i) {  // 读取输入数据
		colors[i] = atoi(vec[i][0].data());
		prices[i] = atoi(vec[i][1].data());
	}
	// O(n * k)
	for (int i = 0; i < k; ++i) {  // i 代表一种颜色
		int j = 0;  // j 代表客栈编号
		int numAfford = 0;
		int numDisford = 0;   // 没有一个词叫 disford,这是我临场造的词,表示“买不起”
		for (; j < n && colors[j] != i; ++j)   // 找到第一家颜色符合要求的客栈
			/* nothing */;
		for (; j < n; ++j) {
			if (colors[j] == i) {   // 客栈的颜色符合要求
				if (prices[j] > p) {  // 无法在客栈配套的咖啡厅消费,拿小伙伴就只能去更前面的客栈
					result += numAfford;
					++numDisford;
				} else {
					numAfford += numDisford;
					numDisford = 0;
					result += numAfford;
					++numAfford;
				}
			} else if (prices[j] <= p) {  // 客栈的颜色不符合要求,但是咖啡厅消费得起,后面就可以在这家咖啡厅消费
				numAfford += numDisford;
				numDisford = 0;
			}
		}
	}
	delete [] colors;
	delete [] prices;
	return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值