USACO历年白银组真题解析 | 2022年12月Circular Barn

学习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
  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值