zoukankan      html  css  js  c++  java
  • P3736 [HAOI2016]字符合并

    链接: https://www.luogu.org/recordnew/lists?uid=62242

    题目描述

    有一个长度为 nn 的 0101 串,你可以每次将相邻的 kk 个字符合并,得到一个新的字符并获得一定分数。得到的新字符和分数由这 kk 个字符确定。你需要求出你能获得的最大分数。

    输入输出格式

    输入格式:

    第一行两个整数 n,kn,k 。

    接下来一行长度为 nn 的 0101 串,表示初始串。输入的的相邻字符之间用一个空格隔开。

    接下来 2^k2k 行,每行一个字符 c_ici 和一个整数 w_iwi , c_ici 表示长度为 kk 的 0101 串连成二进制后按从小到大顺序得到的第 ii 种合并方案得到的新字符, w_iwi 表示对应的第 ii 种方案对应获得的分数。

    输出格式:

    输出一个整数表示答案。

    输入输出样例

    输入样例#1: 复制
    3 2
    1 0 1
    1 10
    1 10
    0 20
    1 30
    
    输出样例#1: 复制
    40

    说明

    第3行到第6行表示长度为 22 的 44 种 0101 串合并方案。 0000 -> 11 ,得 1010 分, 0101 -> 11 得 1010 分, 1010 -> 00 得 2020分, 1111 -> 11 得 3030 分。

    对于 100\%100% 的数据,

    1<=n<=300,0<=ci<=1,wi>=1,k<=8

    题解:区间dp,

    首先我们可以知道最后压成的状态长度不超过k, 所以我们就可以状压最后的状态了;

    dp[i][j][sta]表示把i~j这个区间压成sta的最大收益;我们考虑sta的最后一位是怎么来的,

    dp[i][j][sta] = dp[i][mid][sta>>1] + dp[mid+1][j][sta&1]; 对于长度为1的单独讨论;

    我是直接刷过去的;对于mid~j 这个区间的长度必须是k-1的倍数(这个可以自己画一下, 非常重要,我就一直这里错)

    #include<bits/stdc++.h>
    using namespace std;
    const int M = 305,     N = (1 << 8) + 1;
    #define ll long long
    int a[M], to[N];
    ll w[N], dp[M][M][N], g[3];
    const ll inf = -1e9;
    
    
    int main(){
        int n, k;
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i++)scanf("%1d", &a[i]);
        for(int s = 0; s < (1 << k); s++)scanf("%d%lld", &to[s], &w[s]);
        memset(dp, 0x8f, sizeof(dp));
        for(int i = 1; i <= n; i++)dp[i][i][a[i]] = 0;
        
        for(int L = 2; L <= n; L++)
            for(int i = 1; i <= n - L + 1; i++){
                int j = i + L - 1, len = j - i;
                while(len > k - 1) len -= (k - 1);
                
                for(int mid = j; mid > 0; mid -= k-1){
                    for(int s = 0; s < (1 << len); s++)
                    if(dp[i][mid - 1][s] > inf){
                        if(dp[mid][j][1] > inf) 
                            dp[i][j][s<<1|1] = max(dp[i][j][s<<1|1], dp[i][mid - 1][s] + dp[mid][j][1]);
                        if(dp[mid][j][0] > inf) 
                            dp[i][j][s<<1] = max(dp[i][j][s<<1], dp[i][mid - 1][s] + dp[mid][j][0]);
                        //printf("%d %d %d %d %I64d %I64d
    ",len,i, j, s, dp[i][j][s<<1|1], dp[i][j][s<<1]);
                    }    
                }
                if(len == k-1){
                        g[0] = g[1] = inf;
                        for(int s = 0; s < (1 << k); s++)
                            if(dp[i][j][s] > inf)
                                g[to[s]] = max(g[to[s]], dp[i][j][s] + w[s]);
                        dp[i][j][1] = g[1]; dp[i][j][0] = g[0];
                        //printf("%d %d %I64d %I64d    twice
    ",i, j, dp[i][j][0], dp[i][j][1]);
                    }
    
            }
        
        ll ans = inf;
        for(int s = 0; s < (1 << k); s++)
            ans = max(ans, dp[1][n][s]);
        printf("%lld
    ", ans);
    }
    View Code
  • 相关阅读:
    TopCoder SRM502 Div1 500 贪心 01背包
    TopCoder SRM502 Div1 1000 动态规划
    LOJ#6433. 「PKUSC2018」最大前缀和 状压dp
    Codeforces 830D Singer House 动态规划
    Codeforces 830C Bamboo Partition 其他
    UOJ#275. 【清华集训2016】组合数问题 数位dp
    Codeforces 806 D. Perishable Roads Dijkstra
    UOJ#53. 【UR #4】追击圣诞老人 树链剖分 k短路
    Java第二天——标识符命名规则、Java的知识、快捷键的使用、Scanner获取值的常用方法
    Scanner的例子
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9470056.html
Copyright © 2011-2022 走看看