1 条题解

  • 0
    @ 2025-8-24 21:52:10

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 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
    上传者