zoukankan      html  css  js  c++  java
  • bzoj 4565 字符合并

    Written with StackEdit.

    Description

    有一个长度为 (n)(01) 串,你可以每次将相邻的 (k) 个字符合并,得到一个新的字符并获得一定分数。得到的新字符和分数由这(k) 个字符确定。你需要求出你能获得的最大分数。

    Input

    第一行两个整数(n,k)。接下来一行长度为n的01串,表示初始串。接下来(2^k)行,每行一个字符(c_i)和一个整数(w_i)(c_i)表示长度为(k)(01)串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,(w_i)表示对应的第(i)种方案对应获得的分数。

    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分。

    Hint

    (1leq nleq 300,0leq cileq 1,1leq w_i,kleq8.)

    Solution

    并不会做...于是临摹zxTLEdalao的题解...然后自己似乎懂了一点.

    • 这道题(kleq8),做出一副状压的嘴脸虽然的确也用到了状压,但解决本题的核心在于区间(dp).
    • 考虑令(f[l][r][p])表示将原串([l,r])合并为(t)能获得的最大收益.
    • 显然是满足无后效性的.合并起来之后就不再关心合并过程了.
    • 枚举中间的端点(mid),使得(mid)右边的子串合并起来是(t)的最后一位数字,左边的子串合并起来是(t)前面的数字.
    • 注意到,合并时只能连续的(k)个数字合并,那么可以合并成(1)位数字的子串长度一定为(1,k,2k-1......),枚举(mid)时相差为(k-1)即可.
    • 特殊情况:从(l)(r)恰好有(k)个数,这时我们直接合并,用辅助数组(g)求出此时的(f),
    #include<bits/stdc++.h>
    #define inf (1LL<<60)//防止相加爆long long 
    using namespace std;
    typedef long long ll;
    inline ll read()
    {
    	int out=0,fh=1;
    	char jp=getchar();
    	while ((jp>'9'||jp<'0')&&jp!='-')
    		jp=getchar();
    	if (jp=='-')
    		{
    			fh=-1;
    			jp=getchar();
    		}
    	while (jp>='0'&&jp<='9')
    		{
    			out=out*10+jp-'0';
    			jp=getchar();
    		}
    	return out*fh;
    }
    inline void upd(ll &x,ll y)
    {
    	x=max(x,y);
    }
    const int MAXN=329;
    const int MAXS=(1<<8)+10;
    int a[MAXN];
    int n,k,lim;
    char buf[MAXN];
    ll w[MAXS],c[MAXS]; 
    ll f[MAXN][MAXN][MAXS];//f[l][r][k]:将原串的[l,r]合并为t能获得的最大分数 
    int main()
    {
    	n=read(),k=read();
    	scanf("%s",buf+1);
    	for(int i=1;i<=n;++i)
    		a[i]=(buf[i]=='1');
    	lim=(1<<k)-1;
    	for(int i=0;i<=lim;++i)
    		c[i]=read(),w[i]=read();
    	ll ans=0;
    	for(int i=1;i<=n;++i)	
    		for(int j=1;j<=n;++j)
    			for(int p=0;p<=lim;++p)
    				f[i][j][p]=-inf;
    	for(int i=n;i>=1;--i)
    		for(int j=i;j<=n;++j)
    			{
    				if(i==j)
    					{
    						f[i][j][a[i]]=0;
    						continue;
    					}
    				int len=j-i;
    				len%=k-1;
    				if(!len)
    					len=k-1;
    				for(int mid=j;mid>i;mid-=k-1)
    					for(int op=0;op<(1<<len);++op)
    						{
    							upd(f[i][j][op<<1],f[i][mid-1][op]+f[mid][j][0]);
    							upd(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)
    							upd(g[c[op]],f[i][j][op]+w[op]);
    						f[i][j][0]=g[0];
    						f[i][j][1]=g[1];
    					}
    			}
    	for(int i=0;i<=lim;++i)
    		upd(ans,f[1][n][i]);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    帕累托分布(Pareto distributions)、马太效应
    Generalized normal distribution and Skew normal distribution
    Secondary NameNode 的作用
    127.0.0.1和0.0.0.0地址的区别
    50070只有本机可以访问(除本机外无法访问)
    SecureCRT SSH 语法高亮
    深入理解VMware虚拟机网络通信原理
    CentOS Virtual Machine 设置SSH主机登录
    路由器(交换机)的光口和电口
    ECC校验
  • 原文地址:https://www.cnblogs.com/jklover/p/9997386.html
Copyright © 2011-2022 走看看