USACO 2022 December Contest,Gold题解

Prob1

(Analysis by Timothy Feng)

Define dp[i][j][k]dp[i][j][k] to be the maximum amount of popularity Bessie can achieve with her friends 1…i1…i, jj moonies, and kk ice cream cones.

  • If Bessie does not want to bribe cow ii, then we can update dp[i+1][j][k]=dp[i][j][k]dp[i+1][j][k]=dp[i][j][k].
  • If Bessie chooses to bribe cow ii, she can optionally spend some ice cream cones to decrease her cost. Loop through 0…k0…k to brute force how many ice cream cones Bessie will spend on cow ii. If Bessie chooses to spend cc cones, then Bessie needs to spend Ci−⌊cXi⌋Ci−⌊cXi⌋ moonies. Therefore, dp[i+1][j−(Ci−⌊cXi⌋)][k−c]=dp[i][j][k]dp[i+1][j−(Ci−⌊cXi⌋)][k−c]=dp[i][j][k].

However, this code runs in O(NAB2)O(NAB2) time.

To do better, suppose that we already know the set of cows that we plan to take. How do we check that inviting these cows is within our budget? We can do this greedily. Start by not spending any cones at all, and spending only money to invite these cows. This might cost more money than we have. Next, we will try to spend some ice cream cones to reduce the amount of money we need to spend. Note that at this point, we would always choose the cow with the smallest XiXi to decrease the total cost most efficiently. In other words, the cows that we bribe with cones is a prefix of all cows when sorted by XiXi. This observation leads us to the fact that for each jj and kk, to choose a new cow ii, we only have one transition to consider. Sort Bessie's friends by increasing XiXi. Note that if we take cow ii, we want to spend all our ice cream cones first before we move on to spending money, so we would use c=min(k,Ci⋅Xi)c=min(k,Ci⋅Xi) cones and Ci−⌊cXi⌋Ci−⌊cXi⌋ moonies. Due to the O(NAB)O(NAB) states we have, this results in an O(NAB)O(NAB) time dp.

We can further remove one dimension. By the same observation from before - that cones are used to the maximum before moonies - for all dp states, either kk equals zero or jj equals AA. For each ii, we now only consider O(A+B)O(A+B) states, leading us to our final O(N(A+B))O(N(A+B)) solution.

定义 dp[i][j][k]dp[i][j][k] 为贝茜与她的 1…i1…i 个朋友、jj 个月之神和 kk 个冰淇淋获得的最大人气值。

  • 如果贝茜不想收买第 ii 头奶牛,那么我们可以更新 dp[i+1][j][k]=dp[i][j][k]dp[i+1][j][k]=dp[i][j][k]。
  • 如果贝茜选择行贿第 ii 头奶牛,她可以选择花费一些冰淇淋来降低成本。循环遍历 0…k0…k 来暴力计算贝茜将在第 ii 头奶牛身上花费多少冰淇淋。如果贝茜选择花费 cc 个冰淇淋,则她需要花费 Ci−⌊cXi⌋Ci−⌊cXi⌋ 个月之神进行收买。因此,dp[i+1][j−(Ci−⌊cXi⌋)][k−c]=dp[i][j][k]dp[i+1][j−(Ci−⌊cXi⌋)][k−c]=dp[i][j][k]。

然而,这份代码的时间复杂度为 O(NAB2)O(NAB2)。

为了更好地解决问题,假设我们已经知道要考虑的奶牛集合。我们如何检查邀请这些奶牛是否在我们的预算内?我们可以贪心地解决这个问题,从不花费任何冰淇淋开始,只花费金钱邀请这些奶牛,这可能会花费更多金钱。接下来,我们将尝试花费一些冰淇淋来减少我们需要花费的金钱。注意,在这个阶段,我们总是选择 XiXi 最小的奶牛来最有效地降低总成本。换句话说,通过 XiXi 排序之后,使用冰淇淋贿赂的奶牛是所有奶牛的前缀。这个观察让我们得出一个结论:对于每个 jj 和 kk,选择一个新的奶牛 ii,我们只有一个转移需要考虑。按 XiXi 递增排序贝茜的朋友。注意,如果我们选择奶牛 ii,我们会先花光所有的冰淇淋,然后再花费金钱,所以我们会使用 c=min(k,Ci⋅Xi)c=min(k,Ci⋅Xi) 份冰淇淋和 Ci−⌊cXi⌋Ci−⌊cXi⌋ 月之神的费用。由于我们拥有 O(NAB)O(NAB) 种状态,因此这导致了一个 O(NAB)O(NAB) 的时间复杂度。

我们可以进一步去掉一个维度。根据之前的观察,冰淇淋在花尽之前被最大限度地使用,因此对于所有的dp状态,kk 要么等于 00,要么 jj 等于 AA。对于每个 ii,我们现在只考虑 O(A+B)O(A+B) 个状态,这使我们得出最终的 O(N(A+B))O(N(A+B)) 解。

Timothy's C++ code:

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

const int N = 2000 + 1;

int dp[N][2 * N];

void set_max(int &a, int b) {
    if (b > a) a = b;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, moonie, cones;
    cin >> n >> moonie >> cones;

    vector<array<int, 3>> cows(n);
    for (auto &[x, p, c] : cows) {
        cin >> p >> c >> x;
    }
    sort(cows.begin(), cows.end());

    memset(dp, -1, sizeof dp);

    dp[0][moonie + cones] = 0;
    for (int i = 0; i < n; ++i) {
        auto [x, p, c] = cows[i];
        for (int j = 0; j <= moonie + cones; ++j) {
            if (dp[i][j] == -1) continue;

            set_max(dp[i + 1][j], dp[i][j]);
            if (j - c * x >= moonie) {
                set_max(dp[i + 1][j - c * x], dp[i][j] + p);
            } else if (j > moonie) {
                int cost_left = c - (j - moonie) / x;
                if (moonie - cost_left >= 0)
                    set_max(dp[i + 1][moonie - cost_left], dp[i][j] + p);
            } else if (j <= moonie && j - c >= 0) {
                set_max(dp[i + 1][j - c], dp[i][j] + p);
            }
        }
    }

    cout << *max_element(dp[n], dp[n] + moonie + cones + 1) << "n";
}

Nick Wu's Python code:

n, a, b = (int(x) for x in input().split())
dpmoney = [0] * (a+1)
dpcones = [0] * (b+1)
v = sorted([[int(x) for x in input().split()] for _ in range(n)], key = 
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、付费专栏及课程。

余额充值