1. 程式人生 > 其它 >[題解] 石家莊二中集團 3.22 日模擬賽 T1 <folding >合併字串

[題解] 石家莊二中集團 3.22 日模擬賽 T1 <folding >合併字串

題面

Bill想要進行對一串字元進行壓縮,使其使用的字元長度最短。

例如, \(AAAAAAAAAABABABCCD\) 可以壓縮成 \(10(A)2(BA)B2(C)D\).

\(“( )”\) 算2個字串長度

\(n\leq100\)

題解

  • 首先一看,\(DP\) 題,而且是區間 \(DP\)

傳統的 \(DP\) 在這裡肯定是不行的,涉及到求前面係數的長度,因此我們肯定是要寫一個 \(check\) 計算函式的。

這題的複雜度允許我們列舉迴圈節的長度,這兩條可以合併。

int calc(int l, int r, int k) {
	for (int i = 0; i <= r - l; ++ i) {
		if (str[l + i] != str[l + i % k]) return 2147483647;
	}
	if ((r - l + 1) / k == 100) return 5 + f[l][l + k - 1];
	else if ((r - l + 1) / k >= 10) return 4 + f[l][l + k - 1];
	else return 3 + f[l][l + k - 1];
}

看完這段程式碼,我發現題面有 BUG

  • 看一下這段輸入

      PPPPPQQQQQPPPPPQQQQQAAAAABBBBBAAAAABBBBBPPPPPQQQQQPPPPPQQQQQAAAAABBBBBAAAAABBBBBCCCCCDDDDDCCCCCDDDDD
    

我們發現括號是可以巢狀的。

因此這麼寫就沒問題了。

總程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 110;
const int INF = 0x3f3f3f3f;
char str[maxn];
int n,f[maxn][maxn];
void CheckMin(int &X, int Y) {
	X > Y ? X = Y : 0;
}
int calc(int l, int r, int k) {
	for (int i = 0; i <= r - l; ++ i) {
		if (str[l + i] != str[l + i % k]) return 2147483647;
	}
	if ((r - l + 1) / k == 100) return 5 + f[l][l+k-1];
	else if ((r - l + 1) / k >= 10) return 4 + f[l][l+k-1];
	else return 3 + f[l][l+k-1];
}
void check(int l, int r){
	for (int i = 1; i <= r - l + 1; ++ i) {
		if ((r - l + 1) % i == 0) {
			int val = calc(l, r, i);
			CheckMin(f[l][r], val);
		}
	}
}
int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	cin>>(str+1);
	n=strlen(str+1);
	for(int len=1;len<=n;len++){
		for(int i=1;i<=n;i++){
			int j=i+len-1;
			if(j>n)break;
			f[i][j]=len;
			check(i,j);
			for(int k=i;k<j;k++){
				CheckMin(f[i][j],f[i][k]+f[k+1][j]);
			}
		}
	}
	printf("%d",f[1][n]);
	return 0;
}

後記與反思

  • 進行各種 \(DP\) 時,一定要弄清楚我要維護什麼狀態,多少狀態

  • 理清思路再寫程式碼,思路是最重要的。

  • 區間 DP 有時 區間長度 要從 1 開始!!!

\(return \ 0\)