cf981 D. Bookshelves(贪心+dp)


题意:

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

\(1\le k\le n \le 50, 0

思路:

一开始的错误做法:\(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<