USACO历年青铜组真题解析 | 2022年1月Drought

学习C++从娃娃抓起!记录下USACO(美国信息学奥赛)备考青铜组别比赛学习过程中的题目,记录每一个瞬间。

附上汇总贴:USACO历年青铜组真题解析 | 汇总-CSDN博客


【题目描述】

Farmer John 的草地里的草在一场大旱中都干死了。经过数小时的绝望和沉思,Farmer John 想到了一个绝妙的主意,购买玉米来喂养他宝贵的奶牛。

FJ 的 N 头奶牛(1≤N≤10^5)排成一行,队伍中的第 i 头奶牛的饥饿度为 hi(0≤hi≤10^9)。由于奶牛是社会性动物,她们坚持一起进食,FJ 降低奶牛饥饿度的唯一方法是选择两头相邻的奶牛 i 和 i+1 并分别喂她们一袋玉米,令她们的饥饿度各减少 1。

FJ 想将他的奶牛喂至所有的奶牛都具有相同的非负饥饿度。请帮助 FJ 求出他喂奶牛达到上述状态所需的最少玉米袋数,或者如果不可能达到,输出 −1。

【输入】

每个测试用例包含多个独立的子测试用例,必须全部回答正确才能通过整个测试用例。

输入的第一行包含 (1≤T≤100),为你需要求解的子测试用例的数量。

以下是 T 个子测试用例,每个子测试用例包含两行。

第一行包含 N,第二行包含 h1,h2,…,hN。输入保证所有子测试用例的 N 之和不超过 10^5。

每个子测试用例的 N 的值可能不同。

【输出】

输出 T 行,每个测试用例输出一行。

【输入样例】

5
3
8 10 5
6
4 6 4 4 6 4
3
0 1 0
2
1 2
3
10 9 9

【输出样例】

14
16
-1
-1
-1

【代码详解】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t, n, a[100005], b[100005];
int main()
{
    cin >> t;  // 输入t
    while (t--) {  // 遍历t次循环
        ll ans=0;  // 定义最少玉米袋数,需使用long long类型
        cin >> n;  // 输入n
        for (int i=1; i<=n; i++) {  // 记录n头奶牛的饥饿度
            cin >> a[i];
        }
        if (n==1) {  // 特判:如果n为1
            cout << 0 << endl;  // 输出0,因为此时无需调整
            continue;
        }
        if (n==2 && a[1]!=a[2]) {  // 特判:如果n为2,且两头牛饥饿度不一样
            cout << -1 << endl;  // 输出-1,因为无法调整
            continue;
        } else if (n==2 && a[1]==a[2]) {  // 如果n为2,且两头牛饥饿度一样
            cout << 0 << endl;  // 输出0,因为无需调整
            continue;
        }
        for (int i=2; i<=n; i++) {  // 从第2头牛开始遍历
            if (a[i]>=a[i-1]) {  // 只要当前牛的饥饿度大于前一头牛
                int delta = a[i]-a[i-1];  // 计算两个之间的差值
                a[i+1] = a[i+1] - delta;  // 下一头牛需要减去这个差值
                a[i] = a[i] - delta;  // 当前的牛也把饥饿度变的和前一头一样
                ans += 2*delta;  // ans增加2个差值
                i = i+1;  // 下次循环i从i+2开始(但这里需设置为i+1,因为还有i++)
            }
        }
        sort(a+1, a+n+1);  // 一轮遍历完后进行从小到大的排序
        int mark=0;  // 定义标记位,准备特判
        for (int i=1; i<=n; i++) {  // 遍历n头牛
            if (a[i]<0) {  // 如果其中有小于0的
                mark=1;  // 则修改mark为1
                break;  // 退出循环
            }
        }
        if (mark==1) {  // 对于一轮遍历调整后小于0的,不符合题意
            cout << -1 << endl;  // 输出-1
            continue;
        }

        int cnt = 0;  // 定义标记位,准备特判
        for (int i=2; i<=n; i++) {  // 遍历n头牛
            if (a[i]>a[i-1]) cnt++;  // 如果属于为连续递增
        }
        if (cnt==n-1) {  // 则cnt为n-1
            cout << -1 << endl;  // 输出-1,因为连续递增的场景无解
            continue;
        }
        int pos = 0;  // 定义标记位,准备特判
        for (int i=1; i<=n; i++) {  // 遍历n头牛
            if (a[i]!=a[1]) {  // 找到第1头和最小值不同的位置
                pos = i;
                break;
            }
        }
        if ((n-(pos-1))%2==1) {  // 如果除最小值外的牛的数量是奇数,也无法调整
            cout << -1 << endl;
            continue;
        }
        for (int i=2; i<=n; i++) {  // 遍历n头牛
            ans += a[i]-a[1];  // 计算每两头牛的差值,并累加,这个就是要减少的饥饿度总和
        }
        if (ans%2==1) {  // 如果总和是奇数
            cout << -1 << endl;  // 也是无解的
            continue;
        } else {  // 只有总和是偶数时,总和还是最终的解
            cout << ans << endl;
        }
    }
    return 0;
}

【运行结果】

5
3
8 10 5
14
6
4 6 4 4 6 4
16
3
0 1 0
-1
2
1 2
-1
3
10 9 9
-1
  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值