Codeforces 981 D.Bookshelves(数位DP)


Codeforces 981 D.Bookshelves
题目大意:
给n个数,将这n个数分为k段,(n,k<=50)分别对每一段求和,再将每个求和的结果做与运算(&)。求最终结果的最大值。
思路:
将答案ans二进制表示,按位枚举,从最高位i开始,检查ans|(1<<i)是否能够得到,如果能,则ans|=1<<i,向低位继续枚举;不能,就直接继续向低位枚举。
检查 ans|(1<<i)能否得到:每次用DP的方式寻找,dp[i][j]表示将前i个数分为j段,是否每一段的和的二进制表示都包含x,如果是,那么肯定能够j段进行与运算得到x,具体方法见代码注释。
注意位运算的优先级,不要漏掉括号,以及不要忘记每次DP前初始化DP数组。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<map>
#include<stack>
#include<queue>
#include<set>
#include<cmath>
#include<algorithm>
#include<climits> 
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
typedef map<int,int> M;
typedef queue<int> Q;
typedef set<int> S;
typedef vector<int> V;
const int maxn=100;
bool dp[maxn][maxn];
ll pre[maxn];
bool is_exist(ll x,int n,int m)
{
    memset(dp,0,sizeof(dp));
    for (int i=1;i<=n;++i)
    {
        if ((pre[i]&x)==x) //对于只分一段的情况,判断和是否包含x即可
            dp[i][1]=1;
        for (int j=1;j<i;++j) //只要有一个j满足:在(j,i]的和包含x的情况下,前j个数分为k-1段可以做到“每一段的和都包含x”(即dp[j][k-1]为真),那么dp[i][k]也为真
        {
            if (((pre[i]-pre[j])&x)!=x) //如果(j,i]这一段的和不包含x,那么直接跳过
                continue;
            for (int k=2;k<=min(i,m);++k) 
            {
                dp[i][k]|=dp[j][k-1];
            } 
        }
    }
    return dp[n][m];
}
int main()
{
    int i,j,m,n,k;
    cin>>n>>k;
    for (i=1;i<=n;++i)
    {
        ll a;
        cin>>a; 
        pre[i]=pre[i-1]+a;
    }
    ll ans=0;
    for (i=60;i>=0;--i) //从高位枚举
    {
        if (is_exist(ans|(1ll<<i),n,k))
            ans|=1ll<<i;
    }
    cout<<ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/orangee/p/9116238.html
981