第9章 第3节 动态规划经典题(C++版)
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
【参考程序】 #include<cstdio> long long a[11][11],f[11][11]; long long s; int n,i,k,k1,j; int max(int a, int b) { return a>b?a:b; } int main() { scanf("%d%d",&n,&k1); scanf("%lld",&s); for (i=n; i>=1; i--) { a[i][i]=s%10; s/=10; } for (i=2; i<=n; i++) for (j=i-1;j>=1;j--) a[j][i]=a[j][i-1]*10+a[i][i];
for (i=1; i<=n; i++) f[i][0]=a[1][i];
//预处理不加乘号的情况
for (k=1; k<=k1; k++)
for (i=k+1; i<=n; i++) for (j=k; j<i; j++) f[i][k]=max(f[i][k],f[j][k-1]*a[j+1][i]); printf("%lld\n",f[n][k1]); return 0;
【参考程序1】 #include<cstdio> int max(int a,int b){return a>b?a:b;} int a[51][51]; int sum[51][51][51][51]; int n,i,j,h,k,x,y,z; int main() { scanf("%d%d%d%d",&n,&x,&y,&z); while(x && y && z) //C++中大于0的正整数都看作true,只有0看作false { a[x][y]=z; scanf("%d%d%d",&x,&y,&z); } for (i=1; i<=n; i++) for (j=1; j<=n; j++) for (h=1; h<=n; h++) for (k=1; k<=n; k++) { int tmp1=max(sum[i-1][j][h-1][k],sum[i][j-1][h][k-1]); int tmp2=max(sum[i-1][j][h][k-1],sum[i][j-1][h-1][k]); sum[i][j][h][k]=max(tmp1,tmp2)+a[i][j]; if (i!=h && j!=k) sum[i][j][h][k]+=a[h][k]; } printf("%d\n",sum[n][n][n][n]); return 0; }
【参考程序】 #include<cstdio> #include<cstring> int min(int a,int b) { return a > b ? b:a; // 三目运算符,相当于if(a>b) return b; else return a; } int f[101][101]; int s[101]; int n,i,j,k,x; int main() { scanf("%d",&n); for (i=1; i<=n; i++) { scanf("%d",&x); s[i]=s[i-1]+x; } memset(f,127/3,sizeof(f)); //赋值127是很大的正数,若无/3后面的相加 for (i=1; i<=n; i++) f[i][i]=0; //可能超出int的范围 for (i=n-1; i>=1; i--) for (j=i+1; j<=n; j++) for (k=i; k<=j-1; k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]); printf("%d\n",f[1][n]); return 0; }
【例9-21 】方格取数 【问题描述】 设有N×N的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则 放入数字0。如下图所示:
某人从图中的左上角的A出发,可以向下行走,也可以向右行走,直到达右下 角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。 【编程任务】 此人从A点到B点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。 【输入格式】Pane.in 输入文件Pane.in第一行为一个整数N(N≤10),表示N×N的方格图。 接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、 该列上所放的数。一行0 0 0表示结束。 【输出格式】Pane.out 输出文件Pane.out包含一个整数,表示两条路径上取得的最大的和。
【输入样例】 7 13 7 8 16 21 4 18
【输出样例】 239
s[i]表示前i堆石头的价值总和,f[i][j]表示把第i堆到
第j堆的石头合并成一堆的最优值。 for (i=n-1;i>=1;i--) for (j=i+1;j<=n;j++) for (k=i;k<= j-1;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]); 输出f[1][n]
第九章 动态规划
第一wk.baidu.com 动态规划的基本模型
第二节 背包问题
第三节 动态规划经典题
动态规划程序设计是对解最优化问题的一种途径、一种方法, 而不是一种特殊算法。不象前面所述的那些搜索或数值计算那样, 具有一个标准的数学表达式和明确清晰的解题方法。动态规划程序 设计往往是针对一种最优化问题,由于各种问题的性质不同,确定 最优解的条件也互不相同,因而动态规划的设计方法对不同的问题, 有各具特色的解题方法,而不存在一种万能的动态规划算法,可以 解决各类最优化问题。因此读者在学习时,除了要对基本概念和方 法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建 立模型,用创造性的技巧去求解。我们也可以通过对若干有代表性 的问题的动态规划算法进行分析、讨论,逐渐学会并掌握这一设计 方法。
第四三节 动态规划经典题
【例9-18】、合并石子 【问题描述】 在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。 规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数 记为该次合并的得分。 【编程任务】 试设计一个程序,计算出将N堆石子合并成一堆的最小得分。 【输入格式】 第一行为一个正整数N (2≤N≤100); 以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数 (1≤i≤N)。 【输出格式】 为一个正整数,即最小得分。
}
【例9-20】编辑距离 【问题描述】 设A和B是两个字符串。我们要用最少的字符操作次数,将字符串 A转换为字符串B。这里所说 的字符操作共有三种: 1、删除一个字符; 2、插入一个字符; 3、将一个字符改为另一个字符。 【编程任务】 对任的两个字符串A和B,计算出将字符串A变换为字符串B所用的最少字符操作次数。 【输入格式】edit.in 第一行为字符串A;第二行为字符串B;字符串A和B的长度均小于200。 【输出格式】edit.out 只有一个正整数,为最少字符操作次数。 【输入样例】 sfdqxbw gfdgw 【输出样例】 4 【算法分析】 状态:f[i][j]记录ai与bj的最优编辑距离 结果:f[m][n],其中m、n分别是a、b的串长 初值:b串空,要删a串长个字符;a串空,要插b串长个字符 转移方程:当a[i]=b[j]时,f[i][j]=f[i-1][j-1],否则, f[i][j]=min(f[i-1][j-1]+1,f[i][j-1]+1,f[i-1][j]+1) 说明:f[i-1][j-1]+1:改a[i]为b[j]; f[i][j-1]+1:a[i]后插入b[j-1]; f[i-1][j]+1:删a[i]。
【算法分析】 此题满足动态规划法的求解标准,我们把它按插入的乘号
数来划分阶段,若插入K个乘号,可把问题看做是K个阶段的 决策问题。设f[i][k]表示在前i位数中插入K个乘号所得的最大 值,a[j][i]表示从第j位到第i位所组成的自然数。用f[i][k]存储阶 段K的每一个状态,可以得状态转移方程: f[i][k]=max{f[j][k-1]*a[j+1][i]}(k<=j<=i) 边界值: f[j][0]=a[1][j] (1<j<=i) 根据状态转移方程,我们就很容易写出动态规划程序: for(k=1;k<=r;k++) //r为乘号个数 for( i=k+1;i<=n;i++) for (j=k;j<i;j++) if (f[i][k]<f[j][k-1]*a[j+1][i]) f[i][k]=f[j][k-1]*a[j+1][i]
【参考程序】 #include<cstdio> #include<cstring> int min(int a,int b){return a<b?a:b;} int f[202][202]; char s1[202], s2[202]; int i,j,k,m,n; int main() { scanf("%s%s",s1,s2); m=strlen(s1); n=strlen(s2); for (i=1; i<=m; i++) f[i][0]=i; //到i位置为止把字符串A的内容全部删除 for (i=1; i<=n; i++) f[0][i]=i; //在开头给字符串A添上和B到i位置相同的字符 for (i=1; i<=m; i++) for (j=1; j<=n; j++) if (s1[i-1]==s2[j-1]) f[i][j]=f[i-1][j-1]; else f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1; printf("%d\n",f[m][n]); return 0; }
【例9-19】乘积最大 【问题描述】 今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著
名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了 一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参 加。活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部 分,找出一种分法,使得这K+1个部分的乘积最大。 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子: 有一个数字串:312, 当N=3,K=1时会有以下两种分法: 1)3*12=36 2)31*2=62 这时,符合题目要求的结果是:31*2=62。 现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。 【输入格式】mul.in 第一行共有2个自然数N,K(6≤N≤40,1≤K≤6) 第二行是一个长度为N的数字串。 【输出格式】mul.out 输出所求得的最大乘积(一个自然数)。 【输入样例】 【输出样例】 4 2 62 1231
【输入样例】 8 2 3 13 266 357 4 4 14 5 2 21 564 6 3 15 7 2 14 000 【输出样例】 67 【算法分析】 一个四重循环枚举两条路分别走到的位置。由于每个点均从上或左继承而来,故 内部有四个if,分别表示两个点从上上、上左、左上、左左继承来时,加上当前两 个点所取得的最大值。a[i][j]表示(i,j)格上的值,sum[i][j][h][k]表示第一条路走到(i,j), 第二条路走到(h,k)时的最优解。例如,sum[i][j][h][k] = max{sum[i][j][h][k], sum[i1][j][h-1][k] + a[i][j] + a[h][k]},表示两点均从上面位置走来。 当(i,j) <> (h,k))时 sum[i][j][h][k]:=max{sum[i-1][j][h-1][k],sum[i][j-1][h][k-1],sum[i-1][j][h][k-1],sum[i][j1][h-1][k]}+a[i][j]+a[h][k]; 当(i,j) = (h,k)时 sum[i][j][h][k]:=max{sum[i-1][j][h-1][k],sum[i][j-1][h][k-1],sum[i-1][j][h][k-1],sum[i][j1][h-1][k]}+a[i][j];