学习C++从娃娃抓起!记录下USACO(美国信息学奥赛)备考白银组别比赛学习过程中的题目,记录每一个瞬间。
附上汇总贴:USACO历年白银组真题解析 | 汇总-CSDN博客
【题目描述】
Farmer John 和他的死对头 Farmer Nhoj 在一个环形牛棚内玩游戏。牛棚内有 N(1≤N≤10^5) 个房间,第 i 个房间初始时内有 ai 头奶牛 (1≤ai≤5×10^6)。游戏玩法如下:
- 两位农夫将总是在同一个房间里。进入一个房间后,每位农夫各执行一次行动,Farmer John 在先。两位农夫在游戏开始时进入房间 1。
- 如果当前房间中的奶牛数量为零,则此时执行行动的农夫即失败。否则,执行行动的农夫选择一个整数 P,其中 P 为 1 或一个不超过当前房间中奶牛的数量的质数,并从当前房间中移除 P 头奶牛。
- 两位农夫均完成行动之后,两位农夫移动到环形牛棚的下一间房间。也就是说,如果农夫们在房间 i,那么他们会移动到房间 i+1,除非他们在房间 N,在这种情况下他们会移动到房间 1。
当两位农夫均采用最优策略时,求获胜的农夫。
【输入】
输入包含 T 个子测试用例。输入的第一行包含 T(1≤T≤1000)。下面是 T 个子测试用例。
每个子测试用例的第一行包含 N,第二行包含 a1,⋯,aN。
输入保证所有 N 之和不超过 2×10^5。
【输出】
对于每一个子测试用例,输出获胜的农夫,为 Farmer John 或 Farmer Nhoj 之一。
【输入样例】
5
1
4
1
9
2
2 3
2
7 10
3
4 9 4
【输出样例】
Farmer Nhoj
Farmer John
Farmer John
Farmer John
Farmer Nhoj
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int maxA = 5e6+5;
int prime[maxA], cnt;
bool notPrime[maxA];
void getPrime() // 欧拉筛,获得1-maxA之间的所有质数
{
notPrime[1] = true;
for (int x=2; x<maxA; x++) {
if (!notPrime[x]) {
prime[cnt++] = x;
}
for (int j=0; j<cnt && x*prime[j]<maxA; j++) {
notPrime[x*prime[j]] = true;
if (x%prime[j]==0) break;
}
}
}
const int maxN = 1e5+5;
int a[maxN], Round[maxN];
int main()
{
getPrime();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i=1; i<=n; i++) {
cin >> a[i];
if (a[i]==1) { // 1要特判
Round[i] = 1; // 只有1轮
} else if (a[i]%4==0) { // 例如a[i]=8,需要3轮,每轮为2+2
Round[i] = a[i] / 4 + 1;
} else if (a[i]%4==2) { // 例如a[i]=10,也需要3轮,2+2+2+2+2
Round[i] = a[i] / 4 + 1;
} else { // 例如a[i]=91,
int target = 0;
while (notPrime[a[i]-target]) { // 取出8,余下91-8为83(John取83,这样Nhoj就会输)
target += 4;
}
Round[i] = target / 4 + 1; // a[i]=91时,也是3轮,2+2+2+2+83
}
}
int minRound = maxA, minRoom = -1;
for (int i=1; i<=n; i++) { // 求出最小的轮数的房间号
if (Round[i]<minRound) {
minRound = Round[i];
minRoom = i;
}
}
cout << (a[minRoom]%4 ? "Farmer John" : "Farmer Nhoj") << endl; // 如果最小轮数的房间,轮数是4个倍数就是John赢,否则就是Nhoj赢
}
return 0;
}
【运行结果】
5
1
4
Farmer Nhoj
1
9
Farmer John
2
2 3
Farmer John
2
7 10
Farmer John
3
4 9 4
Farmer Nhoj