zoukankan      html  css  js  c++  java
  • 洛谷 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;

    完结撒花

  • 相关阅读:
    Winform中在ZedGraph中最多可以添加多少条曲线(转)
    c#委托的含义和用法
    vs2010打开vs2017工程
    C# Socket编程资源
    C# 调用打印机 打印 Excel (转)
    NPOI 教程
    C# 调用C++ DLL 的类型转换(转载版)(转)
    进程间通信(网络阅读笔记)
    NPOI 第二篇 设置样式与合并单元格(转)
    分布式事务的 6 种解决方案,写得非常好!
  • 原文地址:https://www.cnblogs.com/rpup/p/13719186.html
Copyright © 2011-2022 走看看