【题解】「HAOI2016」字符合并

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

solution:
可以看出本题的解法是区间DP。我们发现,区间[l,r]每次操作的区间不会有交集,因为假设最后只剩了几个点,但这几个点其实是有一个完整的区间变过来的。换句话说,在最后剩下的几个数之中,是由左右两个区间分别独立操作得到的。我们称这样的问题具有可划分性

再看数据 n < = 300 n<=300 n<=300,可以放心大胆地状压。
事实上,区间最后的合并长度是不用写进状态的,为(len-1)%(k-1)+1

几个小优化:

  • 枚举k时可以K-1格地跳,因为1+s*(K-1)最终一定被合并成一个点
  • color枚举时最多为(1<<len2)-1,其中len2为最终长度

时间复杂度 O ( n 2 ⌊ n k ⌋ ∗ 2 k ) O(n^2 \lfloor \frac{n}{k} \rfloor*2^{k} ) O(n2kn2k),这个复杂度虽然看上去和暴力差不多,但实际运行远低于该上界。

细节优化有点多。状压是精髓。

#include <bits/stdc++.h> using namespace std; const int N = 301; const int M = 257; int n, K, c[M], w[M], f[N][N]; long long dp[N][N][M]; //事实上,区间最后的合并长度是不用写进状态的 //区间是不互相包含的 //这也是左右分治的理由 //毕竟左右都在变化,可以看作两个子问题 //亦可看作最终长度的划分 //(len-1)%(k-1)+1; int maxn; char a[N]; signed main() { memset(dp, -0x3f, sizeof(dp)); scanf("%d%d", &n, &K); scanf("%s", a + 1); maxn = (1 << K) - 1; n = strlen(a + 1); for (int i = 0; i <= maxn; i++) { scanf("%d%d", &c[i], &w[i]); } for (int len = 1; len <= K; len++) for (int i = 1; i <= n - len + 1; i++) f[i][i + len - 1] = f[i][i + len - 2] * 2 + a[i + len - 1] - '0'; for (int i = 1; i <= n; i++) dp[i][i][a[i] - '0'] = 0; // for(int i=1;i<=n-K+1;i++) { // int color=f[i][i+K-1]; // dp[i][i+K-1][c[color]]=w[color]; // printf("dp[%lld][%lld][%lld]=%lld\n",i,i+K-1,c[color],w[color]); // } for (int len = 2; len <= n; len++) { for (int i = 1; i <= n - len + 1; i++) { int j = i + len - 1, len2 = (len - 1) % (K - 1) + 1; if (len == K) { int color = f[i][j]; dp[i][j][c[color]] = w[color]; continue; } int _maxn = (len2 == 1 ? maxn : (1 << len2) - 1); for (int k = i; k < j; k += K - 1) { for (int color = 0; color <= _maxn; color++) { int len3 = (k - i) % (K - 1) + 1, len4 = (j - k - 1) % (K - 1) + 1; if (len2 > 1) dp[i][j][color] = max(dp[i][j][color], dp[i][k][color >> len4] + dp[k + 1][j][color % (1 << len4)]); else dp[i][j][c[color]] = max(dp[i][j][c[color]], dp[i][k][color >> len4] + dp[k + 1][j][color % (1 << len4)] + w[color]); } } // if(len2==1) //printf("dp[%lld][%lld][1]=%lld\ndp[%lld][%lld][0]=%lld\n",i,j,dp[i][j][1],i,j,dp[i][j][0]); } } long long res = 0; for (int i = 0; i <= maxn; i++) res = max(res, dp[1][n][i]); printf("%lld", res); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530373.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示