1 条题解
-
0
自动搬运
来自洛谷,原作者为

zxTLE
**搬运于
2025-08-24 21:52:10,当前版本为作者最后更新于2018-04-12 16:30:58,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这道题我觉得真实难度应该在省选/NOI-以下。
主要思路为区间dp+状压dp
由题目的k<=8考虑使用状态压缩。
为了得到最大的分数我们应该尽量合并数字到不能合并为止,于是最终得到的01串中的每一个数字 展开(还原后)是一系列不相交的区间,我们考虑用区间dp的思维来转移状态。
我们用f[ i ][ j ][ t ]表示原串中第i到j个数字最终合并成t的状态的最大分数。
我们枚举中间的断点mid,(i<=mid<=j),上文已证区间不相交,于是不妨使mid右边的子串合并成t的最后一位数字,mid左边的合并成其他位的数字。 因为能合并成1位数字的原串长度可以为1,k,2k-1......,所以我们每次将mid的值改变k-1即可。
另有一种特殊情况,即i到j恰好有k位数字,我们直接把它合并一次。用一个辅助数组存储最大值(直接修改f数组可能会导致修改后的数能够再修改其他数组元素,具体看代码理解一下)。
最后的答案就是f[ 1 ][ n ][所有状态]中的最大值啦。 由于wi的值可能很大,所以记得使用long long。
附代码:
#include<bits/stdc++.h> #define N 310 #define K 8 #define inf (ll)1<<60 #define ll long long using namespace std; ll n,k,a[N],c[1<<K],w[1<<K],f[N][N][1<<K]; int main() { scanf("%lld%lld",&n,&k); for (ll i=1;i<=n;i++) scanf("%1lld",&a[i]); for (ll i=0;i<(1<<k);i++) scanf("%lld%lld",&c[i],&w[i]); for (ll i=1;i<=n;i++) for (ll j=1;j<=n;j++) for (ll z=0;z<(1<<k);z++) f[i][j][z]=-inf; //读入及初始化 for (ll i=n;i>=1;i--) for (ll j=i;j<=n;j++) //注意循环的正逆顺序,因为mid比j小,j正序枚举;mid比i大,i倒序枚举 //(使状态转移时f[i][mid-1][..],f[mid][j][..]的值已被处理好) { if (i==j) { f[i][j][a[i]]=0;continue;} ll len=j-i; len%=k-1; if (!len) len=k-1; for (ll mid=j;mid>i;mid-=k-1) for (ll op=0;op<(1<<(len));op++) { f[i][j][op<<1]=max(f[i][j][op<<1],f[i][mid-1][op]+f[mid][j][0]); f[i][j][op<<1|1]=max(f[i][j][op<<1|1],f[i][mid-1][op]+f[mid][j][1]); } if (len==k-1) { ll g[2]; g[0]=g[1]=-inf; for (ll op=0;op<(1<<k);op++) g[c[op]]=max(g[c[op]],f[i][j][op]+w[op]); f[i][j][0]=g[0];f[i][j][1]=g[1]; } } ll ans=-inf; for (ll i=0;i<(1<<k);i++) ans=max(ans,f[1][n][i]); printf("%lld",ans); }
- 1
信息
- ID
- 1319
- 时间
- 1000ms
- 内存
- 250MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者