P3736 [HAOI2016]字符合并
Posted edsheeran
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 种方案对应获得的分数。
输出格式:
输出一个整数表示答案。
输入输出样例
说明
第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
题解:
#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); }
以上是关于P3736 [HAOI2016]字符合并的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ4565HAOI2016字符合并 [状压DP][区间DP]