CSDN竞赛11期题解

本文分析了一次编程竞赛中涉及的题目,包括圆小艺、K皇把妹、筛选宝物和圆桌问题。题目主要考察阅读理解和模拟实现,如计算同心圆的染色面积、寻找权值范围内最近节点、01背包问题以及座位安排问题。通过排序、贪心算法和动态规划等方法解决这些问题。
摘要由CSDN通过智能技术生成

总结

本次竞赛题目没有出现明显的bug,题目比较简单,大都考察阅读理解能力。除了在 T 1 T_1 T1上被 π \pi π的精度卡了很久,其他题目做起来都比较容易。另外,输入模板部分题目输入读入string后采取了stoi进行转换,部分题目依旧使用string作为模板函数的输入,这点还需要改进。

题目列表

1.圆小艺

题目描述

最近小艺酱渐渐变成了一个圆滑的形状-球!!
小艺酱开始变得喜欢上球!
小艺酱得到n个同心圆。
小艺酱对着n个同心圆进行染色。
相邻的圆范围内不能有相同的颜色。相隔一层的圆颜色相同。
小艺酱想知道两种颜色最大中最外层圆的那种颜色染了多少?

输入描述:

第一行输入整数n.(1<=n<=1000)表示圆的数量。
第二行输入n个圆的半径。(1<=r<=1000)

输出描述:

输出染色面积,保留小数点后3位。

输入样例:

3
1 2 3

输出样例:

18.849

分析

本次竞赛的压轴题,为啥这么说呢?这题卡 π \pi π的的精度。即使写 3.141592653 3.141592653 3.141592653都只能通过九成用例,卡了我很久后改成 a c o s ( − 1 ) acos(-1) acos(1)才AC。

考察阅读理解的模拟题,将圆按照半径排序后,自大而小的计算面积。设同心圆的面积自小而大依次是 S 1 , S 2 , . . . , S n S_1,S_2,...,S_n S1,S2,...,Sn,染色面积等于 S n − S n − 1 + S n − 2 − . . . + ( − 1 ) n + 1 ∗ S 1 S_n -S_{n-1}+S_{n-2}-...+(-1)^{n+1}*S_1 SnSn1+Sn2...+(1)n+1S1

代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const double PI = acos(-1);
double solution(int n, std::vector<int>& vec) {
	sort(vec.begin(),vec.end());
	double res = 0;
	int t = 1;
	for(int i = n - 1; i >= 0; i--) {
		double x = vec[i];
		res += x * x * t;
		t = -t;
	}
	return res;
}
int main() {
	int n;
	std::vector<int> vec;
	std::cin>>n;
	int x;
	for(int i = 0; i < n; i++) {
		cin>>x;
		vec.push_back(x);
	}
	double result = solution(n,vec);
	printf("%.3lf\n",result*PI);
	return 0;
}

2.K皇把妹

题目描述

存在n个节点,目标节点在m。
每个节点有自己的权值a。
在权值k内选择一个权值非0节点且与目标节点距离最近。
节点i与节点j的距离为abs(i-j)。

输入描述:

第一行输入整数n,m,k.(1<=n,m,k<=100)
第二行输入n个整数的权值。(1<=a<=1000)

输出描述:

输出最小距离

输入样例:

7 3 50
62 0 0 0 99 33 22

输出样例:

3

分析

考察阅读理解的模拟题,翻译一下就是有一个线性序列 a 1 , a 2 , . . . , a k , . . . , a n a_1,a_2,...,a_k,...,a_n a1,a2,...,ak,...,an,我们在这个序列里找离 a k a_k ak最近的并且值在 ( 0 , k ] (0,k] (0,k]之间的元素,输出二者之间的距离即可。只需要我们从 a k a_k ak开始向左和向右逐个遍历,找到符合要求的元素为止。

代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int solution(int n, int m, int k, std::vector<int>& vec) {
	int p = m - 1;
	int i = p,j = p;
	while(true) {
		if(i >= 0 && vec[i] && vec[i] <= k) return (p - i);
		if(j < n && vec[j] && vec[j] <= k) return j - p;
		i--,j++;
	}
	return -1;
}
int main() {
	int n;
	int m;
	int k;
	std::vector<int> vec;
	std::cin>>n;
	std::cin>>m;
	std::cin>>k;
	std::string line_0, token_0;
	getline(std::cin >> std::ws,line_0);
	std::stringstream tokens_0(line_0);
	while(std::getline(tokens_0, token_0, ' ')) {
		vec.push_back(std::stoi(token_0));
	}
	int result = solution(n, m, k,vec);
	std::cout<<result<<std::endl;
	return 0;
}

3. 筛选宝物

题目描述

已知存在n个宝物,每个宝物都有自己的质量m和价值v,在考虑选择宝物时只能选择总质量小于等于M的方案,请问在最 优方案下选择宝物,能获取到最大价值V是多少?

分析

01背包问题裸题,具体解析可以参考 A c W i n g   2   01 背 包 问 题 AcWing\ 2\ 01背包问题 AcWing 2 01

代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
int f[105][1005];
int solution(int n, int M, std::vector<std::vector<int>>& vec) {
	for (int i = 1; i <= n; i++) {
		for(int j = 0; j <= M; j++) {
			f[i][j] = f[i-1][j];
			if(j >= vec[i-1][0]) f[i][j] = max(f[i][j],f[i-1][j-vec[i-1][0]] + vec[i-1][1]);
		}
	}
	int res = 0;
	for(int i = 1; i <= M; i++) res = max(res,f[n][i]);
	return res;
}
int main() {
	int n;
	int M;
	std::vector<std::vector<int>> vec;
	std::cin>>n;
	std::cin>>M;
	std::string line, token;
	for (int i = 0; i < n; i++) {
		int a,b;
		cin>>a>>b;
		vec.push_back({a,b});
	}
	int result = solution(n, M,vec);
	std::cout<<result<<std::endl;
	return 0;
}

4.题目名称:圆桌

题目描述

有N个客人与足够多张的圆桌。主人安排每位客人坐在一个圆桌边,但是每位客人希望自己左右边上分别有一些空座位,不然会觉得害羞。注意,如果一个客人所在的圆桌只有他一个人,那么他左边的空座位数量就是他右边的空座位数量。
试问主人需要准备多少个座位,才能让每个客人舒适的坐下。

输入描述:

第一行输入一个整数N,(1<=N<=10000),代表客人的数量
接下来N行,每行两个整数li与ri,(1<=i<=N,1<=li<=ri<=1000000000)
代表第i位客人希望左边有li个空座位,右边有ri个空座位。

输出描述:

输出一个整数,代表主人需要准备的最少座位数量。

输入样例:

3
1 1
1 1
1 1

输出样例:

6

分析

贪心的问题总是代码简洁,但是证明复杂。首先,要注意题目中的有足够多圆桌的含义,这意味着最多可以准备n张圆桌,让每个人都独坐一桌。对于第一个客人而言,要么独坐一桌,要么与他人合坐。独坐一桌的话,除了他自己坐的椅子,还需要放上 m a x ( l 1 , r 1 ) max(l1,r1) max(l1,r1)个椅子;与他人合坐的话,假设第二个人坐在他右边,那么,其右边最少需要放 m a x ( l 2 , r 1 ) max(l2,r1) max(l2,r1)个椅子。不论是哪种情况,某个桌子上,若干个客人坐成一圈,每个人右边的椅子数量都取决于其希望右边放置的椅子数量和右边客人希望左边放置的椅子数量的大小。如果 j j j坐在 i i i右边,我们就说 r i r_i ri l j l_j lj是配对的,配对的椅子数量是 k   =   m a x ( r i , l j ) k\ =\ max(r_i,l_j) k = max(ri,lj)。例如 r i = 3 ,   l j = 5 r_i=3,\ l_j=5 ri=3, lj=5。那么在中间放置5张椅子,对于 i i i而言,有两张椅子是多余的,我们希望这种多余的椅子越少越好,对于每个客人而言,就是希望与其 r r r配对的 l l l之间越接近越好。

但是天不遂人愿,如果有一个 l l l,有两个 r r r都想与之配对,那么注定有一人要失望了。比如 l = 3 l=3 l=3,有两个 r r r分别是4和5的两个客人,离他们最近的 l l l就是3,那么要使用什么策略才能让准备的椅子最少呢?假设 l l l r r r的集合里分别有 l 1 l_1 l1 l 2 l_2 l2 r 1 r_1 r1 r 2 r_2 r2(这些 l l l r r r可以来自不同的客人)。什么样的排列顺序需要的椅子最少呢?有以下两种策略:

  • l 1 l_1 l1 r 1 r_1 r1配对,需要额外添加的椅子数等于 a = m a x ( l 1 , r 1 ) + m a x ( l 2 , r 2 ) a=max(l1,r1) + max(l2,r2) a=max(l1,r1)+max(l2,r2)
  • l 1 l_1 l1 r 2 r_2 r2配对,需要额外添加的椅子数等于 b = m a x ( l 1 , r 2 ) + m a x ( l 2 , r 1 ) b=max(l1,r2) + max(l2,r1) b=max(l1,r2)+max(l2,r1)

不妨设其中最大值是 r 1 r1 r1,那么 a = r 1 + m a x ( l 2 , r 2 ) a=r1 + max(l2,r2) a=r1+max(l2,r2) b = r 1 + m a x ( l 1 , r 2 ) b=r1+max(l1,r2) b=r1+max(l1,r2),再来讨论下次大值。

  • 如果次大值是 r 2 r2 r2,那么 a a a b b b的值都是 r 1 + r 2 r1+r2 r1+r2,两种策略需要的椅子数相同;
  • 如果次大值是 l 2 l2 l2,那么 a = r 1 + l 2 a=r1+l2 a=r1+l2,显然 a > b a>b a>b,第二种策略最优;
  • 如果次大值是 l 1 l1 l1,那么 b = r 1 + l 1 b=r1+l1 b=r1+l1,显然 a < b a<b a<b,第一种策略最优。

注意上面的最优策略,第二种情况下,最优策略是最小的 l l l与最小的 r r r配对;第二种情况下,最优策略是最大的 l l l与最大的 r r r配对,可以得出最优策略就是小的 l l l与小的 r r r配对,大的 l l l与大的 r r r配对。

所以本题的最优策略也就是: l l l集合和 r r r集合分别排序,排好序的 l l l r r r依次配对。如果配对的 l l l r r r来自同一个客人,就让他独坐一桌,否则,让配对的 l l l坐在 r r r的右边。

所以本题贪心的过程可以总结为,从单个最优策略每个 r r r选择与之最接近的 l l l,到局部最优策略按相对顺序配对。每个配对的 l l l r r r表示需要多加 m a x ( l , r ) max(l,r) max(l,r)张椅子,最后再加上这 n n n个人自己坐的椅子就是本题的解了。

代码

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 10005;
int l[N],r[N];
int main() {
	int n;
	std::cin>>n;
	for(int i = 0; i < n; i++) cin>>l[i]>>r[i];
	sort(l,l+n);
	sort(r,r+n);
	long long res = 0;
	for(int i = 0; i < n; i++) res += max(l[i],r[i]) + 1;
	std::cout<<res<<std::endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值