Description:
有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数 。得到的新字
符和分数由这 k 个字符确定。你需要求出你能获得的最大分数 。
Input:
第一行两个整数 n,k 。接下来一行长度为 n 的 01 串,表示初始串 。接下来 行,每行一个字符ci和一个整数wi,ci
表示长度为 k 的 01 串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi 表示对应的第 i 种方案对应
获得的分数 。1 <= n <= 300,0 <= ci <= 1,1 <= wi <= ,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:
我们从数据范围中知道,合并一定比不合并好,最终我们得到的串一定是不能再合并了,而这个不能再合并的串的长度一定是在(0,k)。
我们定义 f [ i ] [ j ] [ sta ] 表示将 i 到 j 这一段合并成 sta 的最大分数 。注意最后一位是由 [ m,j)得到的,所以我们可以枚举断点来转移,需要注意的是 [ m,j)一定要合并成一位,所以需满足区间长度为 1 + ( k - 1 ) * x ( x >= 0 ) 。
状态转移:
f [ i ] [ j ] [ 0 ] = max( f [ i ] [ j ] [ 0 ] , f [ i ] [ m - 1 ] [ sta << 1 ] + f [ m ] [ j ] [ 0 ] );
f [ i ] [ j ] [ 1 ] = max( f [ i ] [ j ] [ 1 ] , f [ i ] [ m - 1 ] [ sta << 1 | 1 ] + f [ m ] [ j ] [ 1 ] );
但是现在有一个问题,我们用 sta 来表示最终状态有一个问题,就是我们将 001,01,1 这些状态全部记成了 1,但显然这样是不对的 。
所以我们用一个数组来记录最终状态是 1 或 0 时的最大分数 。
当最终长度是 1 时,特殊处理一下(详见代码)。
这样做的时间复杂度时 O ( *
) 的,但是实际上合法状态不会很多,所以可以AC 。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 305;
long long INF;
int n, k;
char s[N];
int a[N], c[256];
long long f[N][N][256], g[2], w[N];
int main() {
scanf("%d%d", &n, &k);
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
for (int i = 0; i < (1 << k); i++) scanf("%d%lld", &c[i], &w[i]);
memset(f, 128, sizeof(f));INF = f[0][0][0];
for (int i = 1; i <= n; i++) f[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;
int len = l;
while (len >= k) len -= (k - 1);
for (int m = j; m > i; m -= (k - 1))
for (int s = 0; s < (1 << (len == 1 ? k - 1 : len - 1)); s++)
if (f[i][m - 1][s] != INF) {
if (f[m][j][0] != INF) f[i][j][s << 1] = max(f[i][j][s << 1], f[i][m - 1][s] + f[m][j][0]);
if (f[m][j][1] != INF) f[i][j][s << 1 | 1] = max(f[i][j][s << 1 | 1], f[i][m - 1][s] + f[m][j][1]);
}
if (len == 1) {
g[0] = g[1] = INF;
for (int s = 0; s < (1 << k); s++)
if (f[i][j][s] != INF)
g[c[s]] = max(g[c[s]], f[i][j][s] + w[s]);
f[i][j][0] = g[0];f[i][j][1] = g[1];
}
}
long long ans = INF;
for (int s = 0; s < (1 << k); s++) ans = max(ans, f[1][n][s]);
printf("%lld\n", ans);
return 0;
}