USACO铜组三道题解析
第一题:ABC问题
给定七个整数,它们是 A、B、C、A+B、B+C、C+A、A+B+C 的某种排列,要求还原出 A、B、C 的值(满足 A ≤ B ≤ C)。
由于所有数均为正整数,A+B+C 必然是这七个数中最大的。而 A 和 B 分别是我我们能确定的最小和次小的数。排序后,最大值即为三数之和,最小两个数即为 A 和 B,进而可求出 C。
#include <bits/stdc++.h>
using namespace std;
int val[8], sum, first, second, third;
int main() {
freopen("abc.in", "r", stdin);
freopen("abc.out", "w", stdout);
for (int i = 1; i <= 7; i++) {
cin >> val[i];
}
sort(val + 1, val + 8);
sum = val[7];
first = val[1];
second = val[2];
third = sum - first - second;
cout << first << " " << second << " " << third << endl;
return 0;
}
第二题:雏菊花环
给定 N 朵花的花瓣数,统计所有连续子数组中,存在某个元素恰好等于该子数组平均值的子数组个数。
数据范围较小,可以直接枚举所有子区间。利用前缀和快速计算区间和,再判断平均值是否为整数,最后检查该区间内是否存在等于该平均值的元素。
初始的 O(N³) 做法会枚举每个子区间后,再遍历区间内每个元素进行判断。可以优化为 O(N²):在向右扩展右端点时,用哈希表维护当前区间内各花瓣数的出现次数,从而 O(1) 判断平均值是否存在。
#include <bits/stdc++.h>
using namespace std;
int petal[105], prefix[105], n, result;
bool freq[1005];
int main() {
freopen("daisy.in", "r", stdin);
freopen("daisy.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> petal[i];
prefix[i] = prefix[i - 1] + petal[i];
}
for (int left = 1; left <= n; left++) {
memset(freq, 0, sizeof(freq));
for (int right = left; right <= n; right++) {
freq[petal[right]] = true;
int total = prefix[right] - prefix[left - 1];
int len = right - left + 1;
if (total % len != 0) continue;
int avg = total / len;
if (avg <= 1000 && freq[avg]) {
result++;
}
}
}
cout << result << endl;
return 0;
}
第三题:一成不变
在无限大的二维网格上,N 头奶牛初始位于不同位置,分别朝北或朝东移动。每头奶牛每小时吃掉当前格子的草后前进一格。如果目标格子的草已被吃掉(非同时到达),则停止。求每头奶牛吃到的草的数量,或输出 "Infinity"。
关键在于理解阻挡关系:朝东的奶牛只会被朝北的奶牛阻挡,反之亦然。直接模拟会超时,需要利用几何分析。
核心思路:将朝东的奶牛按 y 坐标排序,朝北的奶牛按 x 坐标排序。对于每一对可能相交的东向牛和北向牛,判断它们的路径交点。若东向牛先到达交点,则它能阻挡北向牛;若北向牛先到达,则它能阻挡东向牛。同时到达则互相不阻挡。需要记录每头牛被阻挡的最早时间(即吃到的草数)。
#include <bits/stdc++.h>
using namespace std;
struct Cow {
int x, y, idx, eaten;
Cow(int _x = 0, int _y = 0, int _i = 0) : x(_x), y(_y), idx(_i), eaten(0) {}
};
Cow east[55], north[55];
int finalAns[55];
int cntE, cntN, total;
bool cmpEast(Cow a, Cow b) {
return a.y < b.y;
}
bool cmpNorth(Cow a, Cow b) {
return a.x < b.x;
}
int main() {
freopen("stuct.in", "r", stdin);
freopen("stuct.out", "w", stdout);
cin >> total;
for (int i = 1; i <= total; i++) {
char dir;
int px, py;
cin >> dir >> px >> py;
if (dir == 'E') {
east[++cntE] = Cow(px, py, i);
} else {
north[++cntN] = Cow(px, py, i);
}
}
sort(east + 1, east + cntE + 1, cmpEast);
sort(north + 1, north + cntN + 1, cmpNorth);
for (int i = 1; i <= cntE; i++) {
for (int j = 1; j <= cntN; j++) {
if (east[i].y < north[j].y || east[i].x > north[j].x) continue;
int distE = north[j].x - east[i].x;
int distN = east[i].y - north[j].y;
if (distE == distN) continue; // 同时到达,互不阻挡
if (distN > distE) {
// 北向牛先被阻挡
if (north[j].eaten == 0 || distN < north[j].eaten) {
north[j].eaten = distN;
}
} else {
// 东向牛先被阻挡
if (east[i].eaten == 0 || distE < east[i].eaten) {
east[i].eaten = distE;
}
}
}
}
for (int i = 1; i <= cntE; i++) {
if (east[i].eaten == 0) {
finalAns[east[i].idx] = -1;
} else {
finalAns[east[i].idx] = east[i].eaten;
}
}
for (int i = 1; i <= cntN; i++) {
if (north[i].eaten == 0) {
finalAns[north[i].idx] = -1;
} else {
finalAns[north[i].idx] = north[i].eaten;
}
}
for (int i = 1; i <= total; i++) {
if (finalAns[i] == -1) {
cout << "Infinity" << endl;
} else {
cout << finalAns[i] << endl;
}
}
return 0;
}
注意上述代码需要修正:实际阻挡逻辑需考虑已被其他牛阻挡的情况,即只有当当前牛到达交点的时间严格小于该牛已被记录的停止时间时,才更新。完整的正确实现应在交叉判断时,检查两头牛是否都还未被更早阻挡。