USACO 2023年12月比赛 铜组题解

USACO 12月比赛 铜组题解

基本上都是思维题,同学们开动自己的小脑瓜嗷~


比赛链接http://usaco.org/

第一题:CANDY CANE FEAST

标签:思维、枚举、模拟
题意:给定 n n n头牛的初始高度和 m m m个糖果棒的初始高度,每个糖果棒都要 n n n头牛轮流吃一波过去,牛吃了多长的糖果棒,就会长多高。但是每头牛只能吃到小于等于它高度并且有的糖果棒。求最后这 m m m个糖果棒都吃完,这 n n n头牛的高度。( 1 < = n , m < = 2 ∗ 1 0 5 1<=n,m<=2*10^5 1<=n,m<=2105,牛和糖果棒高度范围: [ 1 , 1 0 9 ] [1,10^9] [1,109])
先通过样例,再深入理解一下题意。
给定 3 3 3 头牛,每头牛的初始高度分别为 3 、 2 、 5 3、2、5 325;给定 2 2 2个糖果棒,每个糖果棒的初始高度分别为 6 、 1 6、1 61
第一个糖果棒情况:一开始糖果棒高度 [ 1 , 6 ] [1,6] [1,6],第一头牛(高度为 3 3 3)吃掉 [ 1 , 3 ] [1,3] [1,3]部分,还剩 [ 4 , 6 ] [4,6] [4,6]部分;第二头牛高度(高度为 2 2 2)太低,吃不到;第三头牛(高度为 5 5 5)能吃到糖果棒的 [ 4 , 5 ] [4,5] [4,5]部分。
牛的高度变成 6 、 2 、 7 6、2、7 627
第二个糖果棒情况:第一头牛(高度为 6 6 6),能把第二个糖果棒 [ 1 , 1 ] [1,1] [1,1]全部吃掉,增长高度 1 1 1
牛的高度变成 7 、 2 、 7 7、2、7 727
题解:把题意理解之后,其实就是一个模拟题。可以维护一下糖果棒目前可以吃的高度区间 [ l , r ] [l,r] [l,r]。当牛的高度完全大于等于 r r r(即能够把糖果棒全部吃掉),那就把目前糖果棒能吃的部分全部加到牛的高度上;如果牛的高度是在 [ l , r ] [l,r] [l,r]中间部分的,那么牛能吃的部分就是牛的高度 a [ j ] a[j] a[j]减去 l l l。注意在枚举的过程中记得维护一下 l l l。( r r r一开始就定好了,没必要去更改)
代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll n, m, l, r, k, a[200005];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= m; i++) {
        cin >> r;
        l = 1;
        for (int j = 1; j <= n; j++) {
            if (a[j] >= r) {
                a[j] += r - l + 1;
                break;
            }
            else if (a[j] >= l) {
                k = a[j];
                a[j] += a[j] - l + 1;
                l = k + 1;
            }
        }
    }
    for (int i = 1; i <= n; i++)
        cout << a[i] << endl;
    return 0;
}

第二题:COWNTACT TRACING 2

标签:思维、枚举、模拟
题意:给定长度为 n n n的字符串(只包含 01 01 01),求用初始最少的 1 1 1每天每一个 1 1 1都会把相邻的两个位置都变成 1 1 1,经过若干天后形成给定的这个字符串。( 1 < = n < = 3 ∗ 1 0 5 1<=n<=3*10^5 1<=n<=3105
我们通过样例解释一下,比如要形成长度为 5 5 5 11111 11111 11111字符串。
可以一开始在第三个位置放 00100 00100 00100,第一天: 01110 01110 01110,第二天: 11111 11111 11111,所以初始只要一个 1 1 1
题解:比较容易想到我们需要把连续的 1 1 1的个数拆出来。比如 00011101101111001 00011101101111001 00011101101111001=> 3   2   4   1 3 \ 2 \ 4 \ 1 3 2 4 1
首先我们去思考每一团连续的 1 1 1要形成的最多天数是多少?(为什么是最多天数呢,因为天数越多,初始的 1 1 1就越少)不好想的话,那我们举几个例子:
1 1 1 0 0 0
11 11 11 0 0 0天(这里可能有些人会有疑惑,其实中间部分的和左右两边的情况 我们到时候要分类讨论的,这边姑且先考虑中间部分的)
111 111 111 1 1 1
1111 1111 1111 1 1 1
11111 11111 11111 2 2 2
111111 111111 111111 2 2 2
. . . . . . ...... ......
往下不断推下去,能推出最多天数其实是不是就是1的个数减去 1 1 1,再除以 2 2 2。(其实就是从最中间往外扩散的过程)

这是每一团连续的 1 1 1要形成的最多天数,那么我们要让每一团的符合,得去拿每一团的最多天数中的最小值( m i n D a y minDay minDay),让每一团连续的 1 1 1都满足天数要求。
因为每一团要让天数往下降,很简单,只要让初始的 1 1 1多一点就好了。
在求出 m i n D a y minDay minDay的基础上,我们接下来就是去求每一团在这样一个天数下,需要初始的 1 1 1的个数。

举个例子:比如 m i n D a y = 2 minDay=2 minDay=2,某一团连续的 1 1 1 1111111 1111111 1111111,那么我们初始的 1 1 1应该是 0010100 0010100 0010100
这道题还有个需要注意的点是:最左( 1 1 1)和最右( n n n)连续的 1 1 1具有特殊性,最大天数能到达这一团的 1 1 1的个数减一。因为如果最左设置 1 1 1,往左扩散是会碰墙,过不去的;最右同理。
代码

#include <bits/stdc++.h>
using namespace std;

vector<int> v;

int main() {
    int n;
    string s;
    cin >> n >> s;

    int pre = 0, suf = n - 1, cnt = 0, minDay = n + 1;
    while (s[pre] == '1') pre++;
    while (suf > pre && s[suf] == '1') suf--;

    if (pre != 0) {
        v.push_back(pre);
        minDay = min(minDay, pre - 1);
    }
    if (suf != n - 1) {
        v.push_back(n - 1 - suf);
        minDay = min(minDay, n - 2 - suf);
    }

    for (int i = pre; i <= suf; i++) {
        if (s[i] == '1') cnt++;
        else if (s[i] == '0') {
            if (cnt > 0) {
                v.push_back(cnt);
                minDay = min(minDay, (cnt - 1) / 2);
            }
            cnt = 0;
        }
    }
    if (cnt > 0) {
        v.push_back(cnt);
        minDay = min(minDay, (cnt - 1) / 2);
    }

    int ans = 0;
    for (int i = 0; i < v.size(); i++) {
        int k = v[i] / (minDay * 2 + 1);
        if (v[i] % (minDay * 2 + 1)) k++;
        ans += k;
    }
    cout << ans;
    return 0;
}

第三题:FARMER JOHN ACTUALLY FARMS

标签:思维、数学
题意:给定 n n n个数字初始值 h i h_i hi,第 i i i个数字每天会增加 a i a_i ai,再给出包含 0 0 0 n − 1 n-1 n1之间所有数字的数组 t i t_i ti,求满足 对于第 i i i 个数字,正好有 t i t_i ti 个比它更大的数字 的最少天数。

题解:题目要求对于对于第 i i i个数字,正好有 t i t_i ti 个比它更大的数字,那么我们肯定得从最小( t i = 0 t_i=0 ti=0)的数开始,然后第二小的数( t i = 1 t_i=1 ti=1)…,在这个过程中,不断地让相邻的两个数看看能不能在初始值为 h i h_i hi,每天增量为 a i a_i ai的情况下满足后一个数大于前一个数,在这个过程中不断地去求这个天数的最大值。

所以一开始我们得去做个映射:第 t i t_i ti大的数是输入的第 i i i个数。
然后在第 1 1 1小的数、第 2 2 2小的数、第 3 3 3小的数… 的过程中,看看两个数是否不符合当前数大于前一个数不符合,且在当前数增量大于前一个数增量的情况下,去求需要增量增加的天数,因为需要满足所有的,所以维护这边天数的最大值。

需要注意的是,想让结果是大于的情况,可以给被除数(数字初始值只差 + 1 +1 +1),再进行处理。
最后再判定一下,修改完之后的序列是否满足题目中要求的顺序,不满足输出 − 1 -1 1
代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll N = 2e5 + 10;
ll h[N], a[N], t[N], f[N];

int main() {
    ll T, n;
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> h[i];
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 1; i <= n; i++) {
            cin >> t[i];
            f[t[i]] = i;
        }
        ll ans = 0;
        for (int i = 1; i < n; i++) {
            if (a[f[i-1]] > a[f[i]] && h[f[i-1]] < h[f[i]]) {
                ll k = (h[f[i]] - h[f[i-1]] + 1) / (a[f[i - 1]] - a[f[i]]);
                if ((h[f[i]] - h[f[i-1]] + 1) % (a[f[i - 1]] - a[f[i]])) k++;
                ans = max(ans, k);
            }
        }
        for (int i = 1; i <= n; i++) h[i] += ans * a[i];
        bool flag = 1;
        for (int i = 1; i < n; i++) {
            if (h[f[i-1]] <= h[f[i]]) flag = 0;
        }
        if (flag) cout << ans << endl;
        else cout << -1 << endl;
    }
    return 0;
}
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
USACO2022金组是国际在线判题系统USACO的最高级别,题目难度较高,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值