总结
本次竞赛题目没有出现明显的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 Sn−Sn−1+Sn−2−...+(−1)n+1∗S1。
代码
#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;
}