用的区间DP(?,样例过不去,能得50
查看原帖
用的区间DP(?,样例过不去,能得50
495599
CSZD楼主2023/10/2 19:56

上课讲的,不然也不会做蓝题( 我的代码:

#include<bits/stdc++.h>
using namespace std;
int dp[110][110],l;
char s[110];
//—————————————————————————— 
int num(int n){//统计括号前数字位数 
	int sum=0;
	while(n!=0){
		sum++;
		n/=10;
	}
	return sum;
}//
//—————————————————————————— 
bool check(int ll,int rr,int kk){//判断字符串是否成规律 (左,右,规律字符 
	if((rr-ll+1)%(kk-ll+1))return false;//不能整除则成不了规律 
	for(int p=kk+1;p<=rr;){//除去规律字符串,将剩下的一一比对 
		for(int f=ll;f<=kk;f++){//规律字符串 
			if(s[f]!=s[p])return false;//如果不同直接返回 
			p++;//f与p同时++ 
		}
		p++; //多加一位 
	}
	return true;//中途不返回,即为成规律 
}//
//—————————————————————————— 
void DP(){//动规算长度 
	for(int i=l;i>=1;i--){//倒 
		for(int j=l;j>=i;j--){
			int cmp=j-i+1; //正在被计算的字符串长度(压缩前可能比压缩后还要短 
			for(int k=i;k<=j;k++){//规律末字符k 
				if(check(i,j,k)){//成规律 
					cmp=min(cmp,num((j-i+1)/(k-i+1))/*多少组规律字符*/+2+(k-i+1));//数字长+括号+循环长 
				}
			}
			dp[i][j]=cmp;//如果压缩成功则为新,失败则不变 
			for(int k=i;k<=j;k++){//存在分界 
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);//分成两半(已遍历 
			}//(分界不存在新压缩,直接赋值 
		} 
	}
}
int main()
{
	memset(dp,0x3f3f3f3f,sizeof(dp));//初始化 
	scanf("%s", s+1);
	l=strlen(s+1);//往后调一位 
    DP();
    cout<<dp[1][l];
	return 0;
}

老师的代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int dp[N][N]; // DP[i,j] 表示区间 [i,j] 压缩后的长度
int len; // 字符串s总长 
char s[N]; // 字符串
//----------------------------------------------------
int num(int x){ // 数字 x 的多少位
	int ret = 0;
	while(x) x /= 10, ret++;
	return ret;
}
//----------------------------------------------------
// 检查区间 [l,r] 有没有 [l,k] 这个循环节
bool check(int l, int r, int k){ 
	// [l,r] 的长度显然需要是 [l,k] 倍数
	if((r - l + 1) % (k - l + 1) != 0) return false; 
	int ll = k - l + 1; // [l,k] 的长度, 例如 YES的长度 
	int tmp = (r - l + 1) / ll; // 区间是否能表示成 tmp(xxx), 例如 3(YES) 
	for(int i = 2; i <= tmp; i++) // 依次检查每个循环节
		for(int j = 1; j <= ll; j++) // 循环节内比对 
			// 只需要比对每一部分是不是和第一个循环节相同 
			// 左边为第 i 个循环节第 j 个字符的位置
			// 右边为第 1 个循环节第 j 个字符的位置
			if(s[l + (i-1)*ll + j - 1] != s[l + j - 1]) 
				return false; // 不一样
	return true; // 一样
}
//  非记忆化搜索版本 
void DP_work(){ 
	// 区间DP的递推版本,需要保证算某个区间的时候,所有它的子区间都计算过了
	// 区间左端点从大到小枚举,右端点随意 
	for(int L=len;L>=1;L--)
		for(int R=L;R<=len;R++){
			// 现在计算 DP[L][R]
			int tmp = R - L + 1; // 第一种转移 dp = r - l + 1
			for(int k = L; k < R; k++) // 第二种转移 X(S) 
				if(check(L, R, k)){//看看 [l,k] 是不是 [l,r] 的一个循环节
					int len_num = num((R-L+1)/(k-L+1)); /*前面数字的长度*/
					tmp = min(tmp, dp[L][k] + 2/*左右括号*/ + len_num);
				}
			for(int k = L; k < R; k++) // 第三种转移,枚举分界点, 分成 ABAB|AAA 
				tmp = min(tmp, dp[L][k] + dp[k+1][R]);
			dp[L][R] = tmp;
		}
}

int main(){
	scanf("%s", s+1); // 读入字符串
	len = strlen(s + 1); // 获得串长
	memset(dp, 0x3f, sizeof(dp)); // 全设置成 INF
//	printf("%d", DP(1, len)); // 记忆化搜索版本 
	DP_work(); // 非记忆化版本 
	printf("%d", dp[1][len]);
}

但没完全抄(?

2023/10/2 19:56
加载中...