poj1159

假设有指针i,j指向字符串的首尾,即初值为0,n-1。

如果字符相等,则总插入次数等于字符串(i+1,j-1)的总插入次数。

如果字符不相等,则有在i处插入一个和j处相等的字符和在j处插入和i处相等的字符两种选择,选择的依据是字符串(i,j-1)总插入次数和字符串(i+1,j)哪个小。

由此子问题和递推关系都有了。

然后是边界条件。设字符串(i,j)总插入次数是res[i][j]。

注意到i<j,因此只需要计算一半的矩阵。边界有i=j时,res[i][j]=0。

计算每个子问题,最后矩阵右上角res[0][n-1]即为所求。

#include<iostream>
#include<fstream>
using namespace std;
int n;
char str[5001];
short res[5002][5002];
int main(){
	streambuf *backup;   
    ifstream fin;   
    fin.open("data.in");   
    backup = cin.rdbuf();   // back up cin's streambuf   
    cin.rdbuf(fin.rdbuf()); // assign file's streambuf to cin
	
	cin>>n;	
	cin>>str;	
	memset(res,0,sizeof(res));
	for(int i=0;i<n;i++){
		res[i][i]=0;
	}
	for(int i=0;i<n-1;i++){
		res[i][i+1]=(str[i]==str[i+1]?0:1);
	}	
	int x;
	for(int i=2;i<n;i++){
		x=0;
		for(int y=i;y<n;y++){
			if(str[x]==str[y]){
				res[x][y]=res[x+1][y-1];
			}else{
				res[x][y]=min(res[x+1][y],res[x][y-1])+1;
			}
			x++;
		}
	}
	cout<<res[0][n-1]<<endl;
	return 0;
}
但是这样做时间空间消耗都很大。我想到不一定每个子问题都用得到,因为当两个字符相等的时候只用了res[x+1][y-1],那么res[x+1][y]和res[x][y-1]就不用计算了,那么相关的一连串子问题都不用计算了。所以我又自顶向下用了递归,但是还是把结果保存,仍然是dp。

#include<iostream>
#include<fstream>
using namespace std;
int n;
char str[5001];
short res[5002][5002];
short f(int x,int y){
	if(res[x][y]==-1){
		if(str[x]==str[y]){
				res[x][y]=f(x+1,y-1);
		}else{
				res[x][y]=min(f(x+1,y),f(x,y-1))+1;
		}
	}
	return res[x][y];
}
int main(){
	streambuf *backup;   
    ifstream fin;   
    fin.open("data.in");   
    backup = cin.rdbuf();   // back up cin's streambuf   
    cin.rdbuf(fin.rdbuf()); // assign file's streambuf to cin
	
	cin>>n;	
	cin>>str;	
	memset(res,-1,sizeof(res));
	for(int i=0;i<n;i++){
		res[i][i]=0;
	}
	for(int i=0;i<n-1;i++){
		res[i][i+1]=(str[i]==str[i+1]?0:1);
	}	
	cout<<f(0,n-1)<<endl;
	return 0;
}
poj提交上去,效率提升并不大,不过我觉得总体来说应该还是有一定提升效率的作用。

进一步研究空间,可以发现矩阵只用了一半,另一半完全可以舍弃,另外,每回合计算只涉及到与对角线平行的三条线(画矩阵就看出来了),因此res有三行就够了。把上面代码用到的index映射到这三行,三行一直roll着用就可以了。

#include<iostream>
#include<fstream>
using namespace std;
int n;
char str[5001];
short res[3][5002];
int main(){
	//streambuf *backup;   
 //   ifstream fin;   
 //   fin.open("data.in");   
 //   backup = cin.rdbuf();   // back up cin's streambuf   
 //   cin.rdbuf(fin.rdbuf()); // assign file's streambuf to cin
	//
	cin>>n;	
	cin>>str;	
	memset(res,0,sizeof(res));		
	int s,m,e,y;
	s=0;m=1;e=2;	
	for(int i=1;i<n;i++){
		y=i;
		for(int x=0;x<n-i;x++){
			if(str[x]==str[y]){
				res[e][x]=res[s][x+1];
			}else{
				res[e][x]=min(res[m][x],res[m][x+1])+1;
			}
			y++;
		}
		if(i!=n-1){
			s=(s+1)%3;
			m=(m+1)%3;
			e=(e+1)%3;
		}
	}	
	
	cout<<res[e][0]<<endl;
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值