洛谷 p4302的dp中的细节讨论

关于P4302 [SCOI2003]字符串折叠因细节不同所产生的三种做法


本题的思路很简单,区间dp即可,但做题不是为了Ac,故我在此分享3种有细微差别但思路相同的做

法,以便后续遇到同类型题来选择适合自己的方法。


第一种

直接读入字符串,枚举的区间长度不包括起点

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e3+50;
char s[maxn];
int m[maxn];
int dp[maxn][maxn];
bool check(char s[],int n,int len){
	for(int i=len;i<n;i++){
		if(s[i]!=s[i%len]){
			return false;
		}
	}
	return true;
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%s",s);
	int n=strlen(s);
	for(int i=1;i<=9;i++){
		m[i]=1;
	}
	for(int i=10;i<=99;i++){
		m[i]=2;
	}
	m[100]=3;
	memset(dp,inf,sizeof(dp));
	for(int i=0;i<n;i++){
		dp[i][i]=1;
	} 
	for(int l=2;l<=n;l++){
		for(int i=0;i+l-1<n;i++){
			int j=i+l-1;
			for(int k=i;k<=j;k++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);//上界 
			}
			for(int k=i;k<=j;k++){
				int len=k-i+1;
				if((l)%len!=0)
					continue ;
				if(check(s+i,l,len)){
					dp[i][j]=min(dp[i][j],dp[i][k]+2+m[(l)/len]); 
				}
			} 
		}	
	}
	printf("%d",dp[0][n-1]);
	return 0;
} 

第二种

将字符串从下标1开始读入,枚举区间包含当前起点;

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e3+50;
char s[maxn];
int m[maxn];
int dp[maxn][maxn];
bool check(char s[],int n,int len){
	for(int i=len;i<n;i++){
		if(s[i]!=s[i%len]){
			return false;
		}
	}
	return true;
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%s",s+1);
	int n=strlen(s+1);
	for(int i=1;i<=9;i++){
		m[i]=1;
	}
	for(int i=10;i<=99;i++){
		m[i]=2;
	}
	m[100]=3;
	memset(dp,inf,sizeof(dp));
	for(int i=0;i<=n;i++){
		dp[i][i]=1;
	} 
	for(int l=2;l<=n;l++){
		for(int i=1;i+l-1<=n;i++){
			int j=i+l-1;
			for(int k=i;k<=j;k++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
			}
			for(int k=i;k<=j;k++){
				int len=k-i+1;
				if((l)%len!=0)
					continue ;
				if(check(s+i,l,len)){
					dp[i][j]=min(dp[i][j],dp[i][k]+2+m[(l)/len]);
				}
			} 
		}	
	}
	printf("%d",dp[1][n]);
	return 0;
} 

第三种

字符串从下标1读入,枚举区间不包含当前起点。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e3+50;
char s[maxn];
int m[maxn];
int dp[maxn][maxn];
bool check(char s[],int n,int len){
	for(int i=len;i<n;i++){
		if(s[i]!=s[i%len]){
			return false;
		}
	}
	return true;
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%s",s+1);
	int n=strlen(s+1);
	for(int i=1;i<=9;i++){
		m[i]=1;
	}
	for(int i=10;i<=99;i++){
		m[i]=2;
	}
	m[100]=3;
	memset(dp,inf,sizeof(dp));
	for(int i=0;i<=n;i++){
		dp[i][i]=1;
	} 
	for(int l=1;l<n;l++){
		for(int i=1;i+l<=n;i++){
			int j=i+l;
			for(int k=i;k<=j;k++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
			}
            int le=l+1;
			for(int k=i;k<=j;k++){
				int len=k-i+1;
				if((le)%len!=0)
					continue ;
				if(check(s+i,le,len)){
					dp[i][j]=min(dp[i][j],dp[i][k]+2+m[(le)/len]); 
				}
			} 
		}	
	}
	printf("%d",dp[1][n]);
	return 0;
} 

总结

这3种做法只有细微的差别,但为什么要做,还是值得思考一下
的。

这有便于我们更好的理解区间dp;

完结撒花

原文地址:https://www.cnblogs.com/rpup/p/13719186.html