2022.2.25解题报告
T1.划拳
题目描述:
思路:
直接按照题意模拟一边遍即可。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
int ans1, ans2;
int main() {
scanf("%d", &n);
while (n -- ) {
int a, b;
scanf("%d%d", &a, &b);
if (a == b)
continue;
if (a > b) {
int t = a - b;
if (a == 20 && (b == 10 || b == 5))
ans2 += 10;
else
ans1 += t;
} else {
int t = b - a;
if (b == 20 && (a == 10 || a == 5))
ans1 += 10;
else
ans2 += t;
}
}
printf("%d %d", ans1, ans2);
return 0;
}
T2.打怪
题目描述:
思路:
由于打每一个怪兽只会消耗能量值这一个量,不会有其他量改变,而且求的是怪兽的数量,因此直接用贪心的思想将所有怪兽根据打败它所需要消耗的能量从小到大排序,再遍历一遍即可。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 5010;
typedef pair<int, int> PII;
int n, w, A, B;
PII q[N];
int cnt = 0;
int main() {
scanf("%d%d%d%d", &n, &w, &A, &B);
int total = A + B; //最大攻击力
for (int i = 1 ; i <= n ; i ++ )
scanf("%d%d", &q[i].y, &q[i].x); //反过来存,方便直接用sort函数排序
sort(q + 1, q + 1 + n);
for (int i = 1 ; i <= n && w >= q[i].x ; i ++ )
if (q[i].y <= total) {
w -= q[i].x;
cnt ++ ;
}
printf("%d\n", cnt);
return 0;
}
T3.楼梯
题目描述:
思路:
如上图,不难看出当两栋建筑之间的距离缩小时,两条梯子的交点也一定会上升。因此,当两栋建筑之间的距离增大是,交点的高度是会减小的。
这样一来,我们就找到了交点高度的单调性。我们只需要枚举两栋建筑之间的距离,再将这个距离下的交点高度算出来,和c作比较,如果大于c,那么距离就太小了;反之,则是太大了;直到算出来的交点高度大小与c误差不超过0.001。
那么,怎么通过两栋建筑之间的距离来求交点高度呢?
设两栋建筑物之间的距离为s,交点高度为h。
首先,我们先用勾股定理求出两个梯子顶端距离地面的高度,分别记为a和b,则有:
a = sqrt(x2 - s2)
b = sqrt(y2 - s2)
然后,如图,根据相似三角形有:
h / a = n / s
h / b = m / s
变形可得:
n = (h * s) / a
m = (h * s) / b
又因为有m + n = s,所以:
h / a + h / b = 1
即:
1 / a + 1 / b = 1 / h
h = (a * b) / (a + b)
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const double e = 1e-5;
double x, y, c;
double calc(double k) { //计算交点高度,k是两栋建筑之间的距离
double a = sqrt(x * x - k * k), b = sqrt(y * y - k * k);
return a * b / (a + b);
}
int main() {
cin >> x >> y >> c;
double l = 0, r = min(x, y); //两栋建筑之间的高度不能大于任何一个梯子的长度
while (r - l > e) {
double mid = (l + r) / 2;
if (calc(mid) > c)
l = mid; //如果交点高度大于c,两栋建筑之间的距离就过小,左端点移至mid处
else
r = mid;
}
printf("%.3lf", r);
return 0;
}
T4.笨鸟
题目描述:
思路:
由题意可知,经过每一个障碍物后所能抵达的最大高度和最小高度是经过当前障碍物的最大高度向斜上方作直线与下一个障碍物的交点,最小高度同理,即如图,红色矩形表示经过每一个障碍物时所能抵达的区间。
因此,我们只需要求出经过每一个障碍物时能抵达的区间,然后看和这个障碍物的开口区间是否有交集,如果有,就更新经过这个障碍物时能抵达的区间;如果不能,就说明鸟无法抵达终点,就可以输出了。
这样一来,判断能否抵达终点已经解决。现在来看一下怎么求最小值。
假设有一个障碍物的横坐标为x3,经过它的时候纵坐标为y3,如图:
那么设经过了up次按键盘,由于经过了x3秒,所以在上升了up个单位长度的同时鸟还下落了x3 - up个单位长度。所以有:
up - (x3 - up) = y3
即:
up = (x3 + y3) / 2
这样一来,只要纵坐标确定了,就可以求出经过了多少次按键盘的操作了。又因为x3已经确定,要使up最小,只要y3最小即可。
因此这题就是求每一个红色矩形的底部下标(即经过每一个障碍物时所能抵达的区间的下界),求出来后再求一下up即可。
**注意:**事实上,红色矩形内部不是连续的整数。由于按一次键盘比不按一次键盘上升的相对高度是2((x + 1) - (x - 1)),因此在一个红色矩形中,所有能经过的点都是相同奇偶性的,也就是说每一个点之间都是隔了1的。又因为起点纵坐标是0,所以能到达的纵坐标就只有偶数。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 500010;
int n, X;
int ans[N];
int main() {
scanf("%d%d", &n, &X);
int up = 0, down = 0, last = 0; //up是上一个区间的上界,down是上一个区间的下界,last是上一个区间的横坐标
bool flag = true; //表示能否抵达终点
for (int i = 1 ; i <= n ; i ++ ) {
int x, a, b;
scanf("%d%d%d", &x, &a, &b);
a ++, b -- ; //不能碰到区间边界a和b
up += x - last; //更新up和down,求出所能抵达的最大上界和最小下界
down -= x - last;
if ((up & 1) != (b & 1))
b -- ; //如果上一个区间上界和当前区间的上界奇偶性不同,那么纵坐标(b - 1)就不可能到达,b再减一
if ((down & 1) != (a & 1))
a ++ ;
up = min(up, b); //求交集,作出新区间
down = max(down, a);
if (up < down) { //如果区间不存在,那么无法抵达终点
flag = false;
break;
}
ans[i] = (x + down) / 2; //存储
last = x;
}
if (!flag)
puts("Stupid bird!");
else{
for (int i = 1 ; i <= n ; i ++ )
printf("%d\n", ans[i]);
printf("%d\n", ans[n]);
}
return 0;
}