链接: 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 种方案对应获得的分数。
输出格式:
输出一个整数表示答案。
输入输出样例
说明
第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的倍数(这个可以自己画一下, 非常重要,我就一直这里错)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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\n",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\n",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\n", ans); }