Live2D

题解 [HAOI2016]字符合并

题目传送门

Description

有一个长度为 \(n\)\(01\) 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数。

得到的新字符和分数由这 k 个字符确定。你需要求出你能获得的最大分数。

\(n\le 3\times 10^2,k\le 8\)

Solution

我们可以观察到的是,为了最优,我们要尽可量要合并的数不相交,因为这样可以合并尽可能多次。于是,也就是说,我们要最后k-1个数展开在原数组互不相交。

于是,我们可以设 \(f_{l,r,S}\) 表示区间 \([l,r]\) 合并成 \(S\) 的最大贡献。转移式显然,特殊情况就是刚好可以再合并一次。具体见代码。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define ll long long
#define MAXN 305

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

ll f[MAXN][MAXN][256];
int n,k,a[MAXN],c[MAXN],w[MAXN];

signed main(){
	read (n,k);
	for (Int i = 1;i <= n;++ i) read (a[i]);
	for (Int i = 0;i < (1 << k);++ i) read (c[i],w[i]);
	memset (f,0xcf,sizeof (f));
	for (Int l = n;l >= 1;-- l)
		for (Int r = l;r <= n;++ r){
			if (l == r){f[l][r][a[l]] = 0;continue;}
			int len = r - l;len %= k - 1;if (!len) len = k - 1;
			for (Int mid = r;mid > l;mid -= k - 1) 
				for (Int S = 0;S < (1 << len);++ S)
					f[l][r][S << 1] = max (f[l][r][S << 1],f[l][mid - 1][S] + f[mid][r][0]),
					f[l][r][S << 1 | 1] = max (f[l][r][S << 1 | 1],f[l][mid - 1][S] + f[mid][r][1]);
			if (len == k - 1){
				ll g[2] = {-INT_MAX,-INT_MAX};
				for (Int S = 0;S < (1 << k);++ S) g[c[S]] = max (g[c[S]],f[l][r][S] + w[S]);
				f[l][r][0] = g[0],f[l][r][1] = g[1];
			}
		}
	ll ans = 0;
	for (Int S = 0;S < (1 << k);++ S) ans = max (ans,f[1][n][S]);
	write (ans),putchar ('\n');
	return 0;
}
posted @ 2021-02-01 21:26  Dark_Romance  阅读(79)  评论(0编辑  收藏  举报