博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【BZOJ】4565: [Haoi2016]字符合并
阅读量:5260 次
发布时间:2019-06-14

本文共 2664 字,大约阅读时间需要 8 分钟。

4565: [Haoi2016]字符合并

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

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,

 

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$数组。(

Code

 

#include
#define LL long longusing 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;}

 

 

 

转载于:https://www.cnblogs.com/wans-caesar-02111007/p/9796902.html

你可能感兴趣的文章
CodeForces Round #545 Div.2
查看>>
卷积中的参数
查看>>
51nod1076 (边双连通)
查看>>
Item 9: Avoid Conversion Operators in Your APIs(Effective C#)
查看>>
深入浅出JavaScript(2)—ECMAScript
查看>>
STEP2——《数据分析:企业的贤内助》重点摘要笔记(六)——数据描述
查看>>
ViewPager的onPageChangeListener里面的一些方法参数:
查看>>
Jenkins关闭、重启,Jenkins服务的启动、停止方法。
查看>>
CF E2 - Array and Segments (Hard version) (线段树)
查看>>
Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化
查看>>
SIGPIPE并产生一个信号处理
查看>>
CentOS
查看>>
Linux pipe函数
查看>>
java equals 小记
查看>>
爬虫-通用代码框架
查看>>
2019春 软件工程实践 助教总结
查看>>
YUV 格式的视频呈现
查看>>
现代程序设计 作业1
查看>>
在android开发中添加外挂字体
查看>>
Zerver是一个C#开发的Nginx+PHP+Mysql+memcached+redis绿色集成开发环境
查看>>