cf981 D. Bookshelves(贪心+dp)

题意:

把长为n的数组不重不漏地分成K个子段。每个子段的价值为段中所有数的和。求所有子段的价值的或和(按位或)

\(1\le k\le n \le 50, 0<a_i<2^{50}\)

思路:

一开始的错误做法:\(f(i,k,d)\) 记录把前 \(i\) 个数分成 \(k\) 段,二进制最高位是第 \(d\) 位的最大答案。最后答案在 \(f(n,K,all)\) 中取最大值。

这样做没考虑后效性。正解:从高到低,看能否凑出某一位。\(f(i,k)\) 表示把前 \(i\) 个数分成 \(k\) 段能否凑出ans的前几位

const int N = 55;
ll n, K, s[N], ans;

bool f[N][N];
bool ok(ll res)
{
    memset(f, 0, sizeof f);
    f[0][0] = 1;
    for(int k = 1; k <= K; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 0; j < i; j++)
                if(f[j][k-1] && ((s[i]-s[j])&res)==res)
                    f[i][k] = 1;
    return f[n][K];
}

signed main()
{
    cin >> n >> K;
    for(int i = 1; i <= n; i++)
        cin >> s[i], s[i] += s[i-1]; //前缀和

    for(ll i = N; i >= 0; i--)
        if(ok( ans|(1ll<<i) )) ans |= (1ll<<i);

    cout << ans;
}

posted @ 2022-02-12 16:58  Bellala  阅读(74)  评论(0)    收藏  举报