• Codeforces Round #384 (Div. 2) 734E(二分答案+状态压缩DP)


    题目大意

    给定一个序列an,序列中只有1~8的8个整数,让你选出一个子序列,满足下列两个要求

    1.不同整数出现的次数相差小于等于1

    2.子序列中整数分布是连续的,即子序列的整数必须是1,1,1....1,2,2,2.....2,2.......连续分布

    做法:

    那么我们可以二分不同整数出现的次数,假如说二分出现次数是L,那么可以证明有a个整数出现次数是L,有(8-a)个整数出现次数是L-1(同时不难证明L+1比L更优)

    这样每个整数只有两种状态,要么选L个,要么选L-1个。

    压缩决策s,s的第i位表示是否对整数i做出了决策

    再用容器把每一个不同整数出现的序列记录下来,这样就可以进行dp了

    设dp[i][s]表示从原序列扫到前i个数,已经做出了s的决策,出现L次的不同整数个数最大是多少

    枚举对第k个整数做决策,dp[i][s]就可以转移到另外两个状态

    dp[next(k, L-1)][s|(1<<k)] = max(itself, dp[i][s]);

    dp[next(k, L)][s|(1<<k)] = max(itself, dp[i][s]+1);

    其中next(k, L)是指从i开始,往后的第L个k的整数位置

    然后取dp[1~n][(1<<8)-1]中的最大值,返回答案即可

    另外如果最大值小于等于0(注意等于!!),说明不存在解,返回0即可。

    需要注意的是,当l = 2都不可行的时候需要特判处理。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    using namespace std;
    const int maxn = 1111;
    const int inf = 1e9;
    int dp[maxn][1<<8], a[maxn], cur[maxn], n;
    vector <int> in[10];
    int can(int L)
    {
        for(int i = 1; i <= 8; i++) cur[i] = 0;
        memset(dp, 128, sizeof(dp));
        dp[1][0] = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int s = 0; s < (1<<8); s++)
            {
                if(dp[i][s] < 0) continue;
                for(int k = 1; k <= 8; k++)
                {
                    if(s & (1<<(k-1))) continue;
                    if(in[k].size() - cur[k] < L-1) continue;
                    dp[in[k][cur[k] + L - 2]][s|(1<<(k-1))] = max(dp[in[k][cur[k] + L - 2]][s|(1<<(k-1))], dp[i][s]);
                    if(in[k].size() - cur[k] < L)  continue;
                    dp[in[k][cur[k] + L - 1]][s|(1<<(k-1))] = max(dp[in[k][cur[k] + L - 1]][s|(1<<(k-1))], dp[i][s]+1);
                }
            }
            cur[a[i]]++;
        }
        int ans = -inf;
        for(int i = 1; i <= n ; i++) ans = max(ans, dp[i][(1<<8) - 1]);
        if(ans <= 0) return 0;
        return ans * L + (8 - ans)*(L-1);
    }
    
    int main()
    {
        //freopen("a.txt", "r", stdin);
        cin>>n;
        for(int i = 1; i <= n; i++) cin>>a[i];
        for(int i = 1; i <= n; i++) in[a[i]].push_back(i);
        int l = 2, r = n, ans = -inf;
        while(l < r)
        {
            int mid = (l+r)>>1;
            if(can(mid)) l = mid+1;
            else r = mid;
        }
        if(can(2) == 0)
        {
            ans = 0;
            for(int i = 1; i <= 8; i++) if(in[i].size() > 0) ans++;
            cout<<ans<<endl;
        }
        else
        {
            ans = max(can(l), can(l-1));
            cout<<ans<<endl;
        }
    }
  • 相关阅读:
    [转]C#程序无法在64位系统上运行之.NET编译的目标平台
    STM32是否可以跑linux
    [转]C/C++ 实现文件透明加解密
    逻辑运算
    STM32F1和STM32F4 区别
    【转】STM32定时器输出比较模式中的疑惑
    Linux rabbitmq的安装和安装amqp的php插件
    跨境电商常用的物流方式
    linux 木马清理过程
    minerd
  • 原文地址:https://www.cnblogs.com/Saurus/p/6183721.html
Copyright © 2020-2023  润新知