zoukankan      html  css  js  c++  java
  • 【BZOJ】4565: [Haoi2016]字符合并

    4565: [Haoi2016]字符合并

    Time Limit: 20 Sec  Memory Limit: 256 MB
    Submit: 690  Solved: 316
    [Submit][Status][Discuss]

    Description

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

    Input

    第一行两个整数n,k。接下来一行长度为n的01串,表示初始串。接下来2k行,每行一个字符ci和一个整数wi,ci
    表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi表示对应的第i种方案对应
    获得的分数。1<=n<=300,0<=ci<=1,wi>=1,k<=8

    Output

    输出一个整数表示答案

    Sample Input

    3 2
    101
    1 10
    1 10
    0 20
    1 30

    Sample Output

    40
    //第3行到第6行表示长度为2的4种01串合并方案。00->1,得10分,01->1得10分,10->0得20分,11->1得30分。

    Solution

    区间DP+状压DP

    看到$k$的范围容易想到把$k$压起来。看到是算区间贡献容易想到区间DP。

    区间DP需要分段,再合并。我们可以想到把一段区间强制合并成$0/1$,与另一段合并。也就是$dp[i][mid-1][s]+dp[mid][j][0/1]$,将$mid-j$强制合并成$0/1$,因此枚举$mid$分段。

    首先了解一个东西,长度为$len$的一段区间,每次相邻$k$个合并,最后剩下的区间长度是$len%(k-1)?len%(k-1):k-1$

    知道了这一点我们就可以确定$i-(mid-1)$这一段的状态了,然后每次判断转移的两个状态是否合法,转移即可。

    所以我们枚举区间$[i,j]$,如果长度为$k$,就直接合并获得分数,但是注意在转移的时候不要在$f$数组中直接更新。举例说明$a[1]=0 a[2]=1$,所以$f[1][2][1]$是合法的状态,假设$01$可以合并成$0$,那么就可以更新$f[1][2][0]$,但是$f[1][2][0]$一旦成了合法状态,那么在之后枚举到$t=0$的时候,$dp[1][2][0]$又会去更新别的状态,但是这样是不合法的,所以我们要用临时数组来记录值,最后赋值给$f$数组。(by

    Code

     

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    
    int n, k, a[305], c[305];
    char s[305];
    LL dp[305][305][305], w[305];
    
    int main() {
        scanf("%d%d", &n, &k);
        scanf("%s", s + 1);
        for(int i = 1; i <= n; i ++)
            if(s[i] == '1')    a[i] = 1;
            else            a[i] = 0;
        memset(dp, 128, sizeof(dp));
        LL oo = -dp[0][0][0];
        for(int i = 0; i < (1 << k); i ++)
            scanf("%d %lld", &c[i], &w[i]);
        for(int i = 1; i <= n; i ++)    dp[i][i][a[i]] = 0;
        for(int len = 2; len <= n; len ++)
            for(int i = 1; i + len - 1 <= n; i ++) {
                int j = i + len - 1;
                int tot = (j - i) % (k - 1) ? (j - i) % (k - 1) : k - 1;
                for(int mid = j; mid >= i; mid -= (k - 1))
                    for(int s = 0; s < (1 << tot); s ++) {
                        if(dp[i][mid - 1][s] != -oo) {
                            if(dp[mid][j][1] != -oo)    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] != -oo)    dp[i][j][s << 1] = max(dp[i][j][s << 1], dp[i][mid - 1][s] + dp[mid][j][0]);
                        }
                    }
                if(tot == k - 1) {
                    LL g[2];
                    g[0] = g[1] = -oo;
                    for(int s = 0; s < (1 << k); s ++)
                        if(dp[i][j][s] != -oo)
                            g[c[s]] = max(g[c[s]], dp[i][j][s] + w[s]);
                    dp[i][j][1] = g[1], dp[i][j][0] = g[0];
                }
            }
        LL ans = 0;
        for(int i = 0; i < (1 << k); i ++)
            ans = max(ans, dp[1][n][i]);
        printf("%lld", ans);
        return 0;
    }

     

     

  • 相关阅读:
    使用DOM4J生成XML文档的分析和简单实例
    察看数据库索引使用情况
    SQL Server分布式事务模板
    SQLBulkCopy 性能统计
    个人技术发展思路
    在SQLServer 中利用OUTPUT 语句实现删除数据的同时备份数据
    对一段SQL进行语法检查
    LoD 迪米特法则
    开发心得
    实现一个服务的基础结构和管理本地服务和WCF服务的管理器
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9796902.html
Copyright © 2011-2022 走看看