Codeforces 920D Tanks

Posted 救命怀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 920D Tanks相关的知识,希望对你有一定的参考价值。

题目链接

题意

\\(n\\) 个容积无限的水缸,初始时水量为\\(a_1,a_2,...,a_n\\),有一把容积为\\(k\\)的勺子,可以从一个水缸中舀水倒入另一个水缸中。问能否给出操作序列,使得最终某一个水缸中水的容量为\\(V\\).

思路

参考 - 粉兔.

结论

首先,如果\\(\\sum_{i=1}^{n}a_i\\lt V\\),显然不可行。

否则,一旦\\(\\exists p_1,p_2,...,p_t,s.t.(\\sum_{i=1}^{t}a_{p_i})\\%k==V\\%k\\),则我们说,这件事是可行的。

为什么呢?因为一旦可以取到同余的值,那么无论总量是多余还是不足都可以用勺子解决:多了,就往外舀;少了,就从其他缸(\\(i.e.\\)\\(j\\)\\((j!=p_i(i=1,2,...,t)\\)))中补进来。

至于操作的具体细节,暂放一下。

dp

那么该怎么判断是否有上面的条件成立呢?

\\(dp[i][j]\\) 表示前 \\(i\\) 个缸能否取到模数为 \\(j\\) 的值,特别的,

\\[dp[i][j]=\\begin{cases}0,&cannot\\ make\\ it\\cr1,&can\\ make\\ it\\ without\\ a_i\\cr2,&a_i\\ needed\\ to\\ make\\ it\\cr\\end{cases} \\]

\\(dp[n][V\\%k]\\) 即表示前 \\(n\\) 个缸能否取到模数为 \\(k\\) 的值。

构造

现在有了这个结论和中间记录的 \\(dp\\) 值,该怎么推出步骤呢?

注意到,上面的记录过程提供给了我们 \\(p_1,p_2,...,p_t\\),即必须全部取的缸;而其余的缸,不妨记为 \\(q_1,q_2,...,q_s\\)

则可行操作如下:

  1. \\(p1,...,p_{t-1}\\) 缸中的水全部舀入 \\(p_t\\) 中;
  2. \\(q1,...,q_{s-1}\\) 缸中的水全部舀入 \\(q_s\\) 中;
  3. \\(p_t\\)\\(q_s\\) 之间进行多退少补。

注意几种 特殊情况,比如 \\(t=n\\)\\(s=n\\) 的情况:

\\(t=n\\) 即所有的缸都需要,最后总量肯定只会超出,不会不够,并且超出的部分必然是 \\(k\\) 的整倍数。
此时,将所有缸中的水都舀到某一个缸中,再将超出的部分舀到另一个缸中;
(这种情况可以与一般情况合在一起统一处理)。

\\(s=n\\) 即所有的缸都不需要,这是什么回事呢?当 \\(k|V\\) 时就会发生这种情况,即所需要的容积恰好可以用若干勺舀出。
此时,将所有缸中的水都舀到某一个缸中,再将需要的部分舀到另一个缸中。

// 很佩服粉兔啦%%%

Code

#include <bits/stdc++.h>
#define maxn 5010
using namespace std;
typedef long long LL;
int a[maxn], b[maxn], dp[maxn][maxn];
bool flag[maxn];
int main() {
    int n, k, v, sum = 0;
    scanf("%d%d%d", &n, &k, &v);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        sum += (b[i] = a[i]), a[i] %= k;
    }

    if (sum < v) { puts("NO"); return 0; }
    dp[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < k; ++j) {
            if (dp[i-1][j]) {
                dp[i][j] = 1;
                if (!dp[i][(j+a[i])%k]) dp[i][(j+a[i])%k] = 2;
            }
        }
    }
    int tar = v % k;
    if (!dp[n][tar]) { puts("NO"); return 0; }
    puts("YES");
    int ans = 0, fnl1 = -1, fnl2 = -1;
    for (int i = n; i >= 1; --i) {
        if (dp[i][tar]==2) {
            flag[i] = true, (tar += k-a[i]) %= k, ans += b[i];
            if (fnl1==-1) fnl1 = i;
        }
        else if (fnl2 == -1) fnl2 = i;
    }
    if (fnl1==-1) {
        for (int i = 1; i < n; ++i) if (b[i]) {
            printf("%d %d %d\\n", (b[i] + k-1)/k, i, n);
        }
        if (v/k) printf("%d %d %d\\n", v/k, n, 1);
        return 0;
    }
    assert((v-ans) % k == 0);
    int rem = v - ans;
    int S1 = b[fnl1], S2 = b[fnl2];
    for (int i = 1; i <= n; ++i) {
        if (i == fnl1 || i == fnl2 || !b[i]) continue;
        if (!flag[i]) printf("%d %d %d\\n", (b[i]+k-1)/k, i, fnl2), S2 += b[i];
        else printf("%d %d %d\\n", (b[i]+k-1)/k, i, fnl1), S1 += b[i];
    }
    assert((v-S1) % k == 0);
    int cnt = (v-S1) / k;
    if (cnt>0) printf("%d %d %d\\n", cnt, fnl2, fnl1);
    else if (cnt<0) printf("%d %d %d\\n", -cnt, fnl1, 1);
    return 0;
}

以上是关于Codeforces 920D Tanks的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #442 (Div. 2) C. Slava and tanks

Tanks!Tutorial 学习

Slava and tanks 877C

unity 教程Tanks中的Transform.InverseTransformPoint理解

Tanks坦克大战

[Codeforces Round #522 (Div. 2, based on Technocup 2019 Elimination Round 3)][C. Playing Piano](代码片段