BZOJ 1090 - 区间dp

Magic Door

题目大意:

给一个字符串,可以将重复的串缩成x(a),表示x个a,求能缩成的最小长度。

题目分析

区间dp: dp[i][j]表示i~j处理后的最小长度, 则有

\[dp[i][j] = min\{dp[i][k] + dp[k + 1][j] (i <= k < j)\} \]

并且如果i~j能够拆成若干个重复串的话:

\[dp[i][j] = min\{dp[i][k] + 2 + num((j - i + 1) / len)\} \]

len表示重复串的长度,枚举断点更新即可。
我比较喜欢写记忆搜索。

code

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

const int N = 105;
int dp[N][N], len;
char s[N];

inline int num(int x){
	int ret = 0;
	while(x) x /= 10, ret++;
	return ret;
}

inline bool check(int l, int r, int k){
	if((r - l + 1) % (k - l + 1)) return false;
	int ll = k - l + 1, tmp = (r - l + 1) / ll;
	for(int i = 2; i <= tmp; i++)
		for(int j = 1; j <= ll; j++)
			if(s[l + (i - 1) * ll + j - 1] != s[l + (i - 2) * ll + j - 1]) return false;
	return true;
}

inline int DP(int l, int r){
	if(dp[l][r] != -1) return dp[l][r];
	int tmp = r - l + 1;
	for(int k = l; k < r; k++) tmp = min(tmp, DP(l, k) + DP(k + 1, r));
	for(int k = l; k < r; k++)
		if(check(l, r, k)) tmp = min(tmp, DP(l, k) + 2 + num((r - l + 1) / (k - l + 1)));
	return dp[l][r] = tmp;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);
	cin >> s + 1;
	len = strlen(s + 1);
	memset(dp, -1, sizeof dp);
	cout << DP(1, len);
}
posted @ 2017-10-07 23:16  CzYoL  阅读(134)  评论(0编辑  收藏  举报