题目描述
某一天妖怪部落的妖怪们为了争当妖王发生了内斗,每个妖怪都发了疯似的攻击同伴,在这种无休止的战斗里,活到最后的妖怪才可以当妖王。
已知部落里有n个妖怪,每个妖怪具有生命值hp
和攻击力attack
两种属性,且每个妖怪单次攻击的attack
与其hp
相同。例如当一个妖怪m1的hp
为x,被另外一个hp
为y的妖怪m2攻击后,m1的hp
就会变成x-y,妖怪的hp<=0时立刻死亡退出战斗。每一轮战斗中一个存活的妖怪可以选择攻击其他任意一个存活的妖怪一次或者不发动攻击,直到只有一个妖怪存活时战斗结束。
求内斗完后的妖王hp值最低可能是多少。
输入描述:
第一行一个n(1≤n≤105),表示妖怪个数。
第二行每行n个数,第i个数ai表示第i只妖怪的hp值(1≤ai≤109)
输出描述:
一个数,表示妖王最低的hp值。
示例1
输入
3
3 1 2
输出
1
说明
第二只妖怪攻击其他所有妖怪,其他所有妖怪都不攻击,最后只有第二只存活。
示例2
输入
2
4 6
输出
2
说明
4攻击6,变成4 2;
2攻击4,变成2 2;
2攻击2,变成2 0;
解题
- 先放GPT跑一下,跑了两次,均不能得到正确结果,验证为防GPT题目
理解
- 攻击的一方不掉血,那么可以血量最小的将所有的怪物杀死,直接排序得到最小的血量的妖怪------------不出意外是错误的
- 刚刚忽略了一个重要的情况,A(血少)打B(血多)后会出现B后来的血比A小,这样A就不是最小的,就比如A(10)、B(15)----->A(10)、B(5),那么就该交换AB的顺序,然后再进行相减,B(5)、A(10)----->A(5)、B(5)
- 这样重复直到其中一方<=0,一方消失,再引入C,重复此操作直到只剩一个数------------符合题意
编程
按照上面解释编写C++程序
#include <bits/stdc++.h>
using namespace std;
int main() {
//输入内容
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
//首先对妖怪血量进行排序
sort(a.begin(), a.end());
//循环到最后
for (int i = 0; i < n - 1; ++i) {
//互相攻击,只留下最小的非负的数,最后小数在前,大数在后
while (a[i] > 0 && a[i + 1] > 0) {
a[i + 1] -= a[i];
if (a[i] > a[i + 1]) {
swap(a[i], a[i + 1]);
}
}
}
//最后的结果是得到的最后的一个数,前面的数均为0或者小于0
cout << a[n - 1] << endl;
return 0;
}
运行结果是正确的但是验证超时,下面是优化一点后的代码
#include <bits/stdc++.h>
//函数实现辗转相减,返回最小非0数
int calculate(int x, int y) {
int last_min = std::min(x, y);
//辗转相减最后结果一定有一方等于0
while (x != 0 && y != 0) {
if (x % y == 0 || y % x == 0) {
//如果两个数存在倍数关系就直接返回最小值,能够大幅减少运算
return last_min;
}
if (x > y) {
x = x - y;
} else {
y = y - x;
}
last_min = std::min(x, y);
}
return last_min;
}
int main() {
int n;
//输入内容
std::cin >> n;
std::vector<int> numbers(n);
for (int i = 0; i < n; i++) {
std::cin >> numbers[i];
}
//先给结果赋初值
int result = numbers[0];
for (int i = 1; i < n; i++) {
result = calculate(result, numbers[i]);
}
std::cout << result << std::endl;
return 0;
}
最后所有示例通过