1. 程式人生 > >bzoj 1068: [SCOI2007]壓縮1068: [SCOI2007]壓縮

bzoj 1068: [SCOI2007]壓縮1068: [SCOI2007]壓縮

1068: [SCOI2007]壓縮1068: [SCOI2007]壓縮

給一個由小寫字母組成的字串,我們可以用一種簡單的方法來壓縮其中的重複資訊。壓縮後的字串除了小
寫字母外還可以(但不必)包含大寫字母R與M,其中M標記重複串的開始,R重複從上一個M(如果當前位置左邊沒
有M,則從串的開始算起)開始的解壓結果(稱為緩衝串)。 bcdcdcdcd可以壓縮為bMcdRR,下面是解壓縮的過程
另一個例子是abcabcdabcabcdxyxyz可以被壓縮為abcRdRMxyRz。

輸入僅一行,包含待壓縮字串,僅包含小寫字母,長度為n。
長度不超過50.

思路。
我們可以用 f[i][j][0/1]來表示i 到 j 之間,有沒有用放 M。
一開始。
f[i][j][0] = j - i + 1;
f[i][j][1] = j - i + 1;
接著狀態轉移。
f[i][j][0] = min(f[i][j][0],f[i][k][0] + j - k),
這個是不放M 的。
f[i][j][1] = min(f[i][j][1],min(f[i][k][1],f[i][k][0]) + min(f[k+1][j][1],f[k+1][j][0]) + 1);
這個是在第k個位置放了M。
還有一個放 R 的。
f[i][j][0] = f[i][(i+j)/2][0] + 1
這個我們要判斷一下,i 到j 之間是不是偶數,還有是不是重複的。

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

char s[100];
int f[100][100][2];
bool check(int x, int y){
	if ((y-x+1) & 1) return 0;
	int mid = (x + y) / 2;
	for (int i = 1; i <= mid - x + 1; i++)
		if (s[x + i - 1] != s[mid + i]) return 0;
	return 1;
}
int main(){
	int len;
	scanf("%s",s+1);
	len = strlen(s+1);
	for (int i = len; i > 0; i--){
		for (int j = i; j <= len; j++){
			f[i][j][0] = j - i + 1;
			f[i][j][1] = j - i + 1;
			for (int k = i; k < j; k++)
				f[i][j][0] = min(f[i][j][0],f[i][k][0] + j - k);
			for (int k = i; k < j; k++)
				f[i][j][1] = min(f[i][j][1],min(f[i][k][1],f[i][k][0]) + min(f[k+1][j][1],f[k+1][j][0]) + 1);
			if (check(i,j)) f[i][j][0] = f[i][(i+j)/2][0] + 1;

		}
	}
	printf("%d\n",min(f[1][len][0],f[1][len][1]));
	return 0;
}