[CF1579G] Minimal Coverage

Minimal Coverage の 传送门

怎么这个题想不到啊,所以我选择了看题解

不容易发现,如果覆盖长度为 \(x\) 可以,那长度更多的一定可以(这里不盖满整个长度为 \(x\) 的段也可以)。

二分试一试,令当前二分的覆盖长度为 \(len\)

DP 一下,\(f_{i, j}\) 表示放完 \(1 \sim i\) 的线段,结尾能否位置 \(j\)

\(f_{i, j}\) 的转移如下:

  1. \(f_{i-1, j-a_i} \left ( j-a_i \ge 0 \right )\)

  2. \(f_{i-1, j+a_i} \left ( j+a_i \le len \right )\)

时间复杂度:\(O(\sum n \max a \log_2 n)\)

朴素实现:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define N 10005
#define mid (l + r >> 1)
int n, a[N];
bool F[N], G[N];
bool *f = F, *g = G;
void swap()
{
    bool *t = f;
    f = g, g = t;
}
bool check(int lim)
{
    memset(f, 1, sizeof F);
    for (int i = 1; i <= n; ++i)
    {
        swap();
        memset(f, 0, sizeof F);
        for (int j = 0; j <= lim; ++j)
        {
            if (j - a[i] >= 0)
                f[j] |= g[j - a[i]];
            if (j + a[i] <= lim)
                f[j] |= g[j + a[i]];
        }
    }
    for (int i = 0; i <= lim; ++i)
        if (f[i])
            return 1;
    return 0;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%d", a + i);
        int l = 1, r = 2000, it = -1;
        while (l <= r)
            check(mid)
                ? (it = mid, r = mid - 1)
                : (l = mid + 1);
        printf("%d\n", it);
    }
    return 0;
}

这个复杂度看起来有点不稳,可以试试 std::bitset详见)。

#include <bits/stdc++.h>
#define int long long
#define mid (l + r >> 1)
using namespace std;
inline int read()
{
    int f = 0, ans = 0;
    char c = getchar();
    while (!isdigit(c))
        f |= c == '-', c = getchar();
    while (isdigit(c))
        ans = (ans << 3) + (ans << 1) + c - 48, c = getchar();
    return f ? -ans : ans;
}
void write(int x)
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
const int N = 1e4 + 5, M = 2e3 + 5;
int n, a[N];
bitset<M> f, base;
inline bool check(int lim)
{
    base.reset();
    for (int i = 0; i <= lim; ++i)
        base[i] = 1;
    f = base;
    for (int i = 1; i <= n; ++i)
        f = f >> a[i] | f << a[i] & base;
    return f.any();
}
signed main()
{
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    int T = read();
    while (T--)
    {
        n = read();
        for (int i = 1; i <= n; ++i)
            a[i] = read();
        int l = 1, r = 2000, it = -1;
        while (l <= r)
            check(mid)
                ? (it = mid, r = mid - 1)
                : (l = mid + 1);
        write(it), putchar('\n');
    }
    return 0;
}
posted @ 2025-01-24 18:16  SilverLi  阅读(23)  评论(0)    收藏  举报