DP中明明只需要最终结果的啊。。。为啥要浪费个数组空间记住方案选择。。。


拿两个题目分析好了。

1.最大k乘积问题

问题描述:

设I是一个n(n≤10)位的十进制整数。如果将I划分成k段,则可得到k个整数。这k个整数的乘积称为I的一个k乘积。设计一个算法,对于给定的I和k,求出I的最大k乘积及相应的划分方法。

提示:

称整数I的最高位到最低位(个位)依次为第0位至第n-1位, 设I(s,t)表示整数I的从s位开始的t位数字组成的整数,f(i,j)表示I(0,i)的最大j乘积(即整数I最左侧i位的最大j乘积),则f(i,j)可按下式递归计算。

可按上式依次计算:

f(2,2),f(3,2),…,f(n,2)

f(3,3),f(4,3),…,f(n,3)

f(k,k),f(k+1,k),…,f(n,k)

f(n,k)即为整数I的最大k乘积。

用a(i,j)记录使f(i,j)取得最大值的m, 从a(n,k)开始可反推得到I的划分方法。设i为整数当前剩余的位数(初始时i=n), j为剩余整数要划分的段数(初始时 j=k),于是可确定一个划分段为I(a(i,j), i- a(i,j)),划分完后整数剩余i= a(i,j)位, 剩余的划分段数j=j-1, 重复上述过程,直到i>0, j=1为止。

 

输入数据:

文件名:input.txt

第一行2个数,分别是整数I的位数n和划分段数k,用空格分隔;

第二行为整数I。

输出数据

文件名:output.txt

第一行,整数I的最大k乘积;

第二行,整数I的划分情况,共k段,段间用空格分隔。

输入输出样例:

输入:input.txt

6 4

253187

输出:output.txt

59472

2 531 8 7

方法都告诉你了哈~下面先贴代码再解释:


/*

	变量定义说明:
	
	a[i]表示该数的第i位是多少
	digit[i][j]表示该数的第i位到第j位的数是多少,
		如样例中digit[2][4]为531
	dp[i][j]为前i位中,添加了j-1个乘积可以取到的最大值,
		当然,dp[i][1]为前i位中添加0个括号(也就是不添加括号,也就是分成一段)。那么就是digit[1][i]。这就是初始化
	mp[i][j]为前i位中,添加了j-1个乘号的第j-1个乘号放在了哪儿(待会输出方案的时候要用)
*/ 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn=10; 
char s[maxn+10];
int a[maxn+10];
int dp[maxn+10][maxn+10];
int digit[maxn+10][maxn+10];
int mp[maxn+10][maxn+10];

int GetDigit(int i,int j){
	int num=0;
	for(int k=i;k<=j;k++) num=num*10+a[k];
	return num;
}

void PrintAns(int i,int j){
	if (j==1){
		printf("%d",digit[1][i]);
		return;
	}
	int k=mp[i][j];
	PrintAns(k,j-1);
	printf(" %d",digit[k+1][i]);
}

int main(){
	freopen("input.txt","r",stdin);
	int n,m;
	int i,j,k,len;
	while(scanf("%d%d",&n,&m)!=EOF){
		scanf("%s",s);
		for(i=1;i<=n;i++) a[i]=s[i-1]-'0';
		//for(i=1;i<=n;i++) printf("%d\n",a[i]);
		memset(dp,0,sizeof(dp));
		memset(mp,0,sizeof(mp));
		//初始化: 
		for(i=1;i<=n;i++) dp[i][1]=dp[i-1][1]*10+a[i];
		//for(i=1;i<=n;i++) printf("%d\n",dp[i][1]);
		
		//计算digit值:
		for(i=1;i<=n;i++)
			for(j=i;j<=n;j++)
				digit[i][j]=GetDigit(i,j);
		
		//计算dp值:				
		for(len=2;len<=m;len++)
			for(j=len;j<=n;j++)
				for(k=1;k<j;k++)
					if (dp[k][len-1]*digit[k+1][j]>dp[j][len]){
						//如果更大,那么记录值和插入乘号的位置
						dp[j][len]=max(dp[j][len],dp[k][len-1]*digit[k+1][j]);
						mp[j][len]=k;
					}
					
		printf("%d\n",dp[n][m]);
		
		//for(i=1;i<=n;i++)
		//	for(j=1;j<=m;j++)
		//		printf("%d%c",mp[i][j],j==m?'\n':' ');
		
		//for(i=1;i<=n;i++)
		//	for(j=1;j<=m;j++)
		//		printf("%d%c",dp[i][j],j==m?'\n':' ');
		PrintAns(n,m);
		printf("\n");
	}
	return 0;
}

/*
	第一重循环控制的是插入的乘号的个数,从0个乘号到m-1个乘号
	第二重控制的是终点的位置
	第三重控制的是乘号可以插入的地方
	(如果想不懂想想dp[3][2]怎么算:dp[3][2]=max{dp[1][1]*digit[2][3],dp[2][1]*dp[3][3]},再不懂再算几个)

	接下来是输出方案:递归m次即可,每次找到当前步骤的乘号插入的地方递归进入,回溯时候输出该数
	递归语句为查找下一个乘号在哪,该数为digit[当前的乘号插入值][当前的末尾值]
*/ 



2.编辑距离问题。

问题描述:

设A和B是2个字符串(长度不超过255字符),我们要用最少的字符操作将字符串A转换为字符串B。这里所说的字符操作是指:(1)删除一个字符,(2)插入一个字符,(3)将一个字符改为另一个字符。将字符串A转换为字符串B所用的最少字符操作数称为字符串A到B的编辑距离,记为δ(A, B),试设计一个动态规划算法,对任给的2个字符串A和B,计算出它们的编辑距离δ(A, B)。

提示:

设所给的两个字符串为A[1..m]和B[1..n],定义D[i,j]= δ(A[1..i], B[1..j])。单个字符a,b间的距离定义为:

考察从字符串A[1..i]到B[1..j]的变换,有以下几种情况:

(1)将字符A[i]改为字符B[j];需δ(A[i], B[j])次操作;

(2)删除字符A[i];需1次操作;

(3)插入字符B[j];需1次操作;

因此,D[i,j]可递归地定义为:

D[i,j]=min{D[i-1,j-1]+ δ(A[i], B[j]), D[i-1,j]+1, D[i,j-1]+1},c[i,j]= δ(A[i], B[j])

D[i,0]=i,c[i,j]=2,i=0~m;

D[0,j]=j,c[i,j]=3,j=0~n;

其中c[i,j]记录了D[i,j]取最小值的三种情况之一。

最优值δ(A, B)=D[m,n]。从c[m,n]开始可反推出A到B的转换方案。

 

输入数据:

文件名:input.txt

第一行,字符串A;

第二行字符串B;

输出数据

文件名:output.txt

第一行,字符串A与B的编辑距离;

第二行,说明,固定为“[x1y]--将x改为y,[2x]--删除x,[3x]--插入x”

第三行,从字符串A向B的转换过程。

输入输出样例:

输入:input.txt

The book ishere.

Here is thebook.

输出:output.txt

13

[x1y]--将x改为y,[2x]--删除x,[3x]--插入x

[2T][h1H]e[2 ][2b][2o][o1r][k1e] is [3t]he[3 ][3b][3o][r1o][e1k].

 

这题简单在方法都告诉你了,难在过程怎么输出的。方法跟题1一样,认真分析题1解法就自然懂了~下面贴代码:


#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cstring>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=505;
const int maxm=505;
const int INF=99999999;
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)

char a[maxn],b[maxn];
int dp[maxn][maxn];
int mp[maxn][maxn];
/*mp[i][j]:
	0.说明a[i]与b[j]已经匹配 
	1.字母a[i]改为b[j]
	2.插入b[j]
	3.删除a[i]
*/ 

void GetAns(int x,int y){
	if (x<1&&y<1) return;
	if (mp[x][y]==0) GetAns(x-1,y-1);
	else if (mp[x][y]==1){
		GetAns(x-1,y-1);
		printf("[%c1%c]",a[x],b[y]);
	}
	else if (mp[x][y]==2){
		GetAns(x,y-1);
		printf("[3%c]",b[y]);
	}
	else{
		GetAns(x-1,y);
		printf("[2%c]",a[x]);
	}
	return;
}

int main(){
	//input; 
	int n,m,i,j;
	gets(a+1);
	gets(b+1);
	memset(dp,0,sizeof(dp));
	n=strlen(a+1);
	m=strlen(b+1);
	
	if (a[1]==b[1]) dp[1][1]=0;
	else{
		dp[1][1]=1;
		mp[1][1]=1;
	}
	
	for(i=2;i<=n;i++){
		dp[i][1]=dp[i-1][1]+1;
		mp[i][1]=3;
	}
	for(j=2;j<=m;j++){
		dp[1][j]=dp[1][j-1]+1;
		mp[1][j]=2;
	}
	
	for(i=2;i<=n;i++)
	for(j=2;j<=m;j++)
		if (a[i]==b[j]){
			dp[i][j]=dp[i-1][j-1];
			mp[i][j]=0;
		}
		else{
			//dp[i][j]=min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j]))+1;
			if (dp[i-1][j-1]<=dp[i][j-1]&&dp[i-1][j-1]<=dp[i-1][j]){
				dp[i][j]=dp[i-1][j-1]+1;
				mp[i][j]=1;
			}
			else if (dp[i][j-1]<=dp[i-1][j-1]&&dp[i][j-1]<=dp[i-1][j]){
				dp[i][j]=dp[i][j-1]+1;
				mp[i][j]=2;
			}
			else{
				dp[i][j]=dp[i-1][j]+1;
				mp[i][j]=3;
			}
		}
	
	/*for(i=1;i<=n;i++)
	for(j=1;j<=m;j++)
		printf("%d%c",dp[i][j],j==m?'\n':' ');*/
	printf("%d\n",dp[n][m]);
	GetAns(n,m);
	return 0;
}