动态规划:1.相比于递归可以说是避免了重复计算,使运算速度得到提高,动态规划是从确定边界条件开始,这种递推可以看成是递归的逆过程。
4.在确定状态转移方程时,我们应该将数组之前的数当成已知条件来用。和递归一样不用考虑怎么来的。
思考:这个问题可以通过递归来解决,代码如下
public int maxsum(int i,int j,int a[][]) {
int D[][]=new int[4][4];
if(i==a.length-1) {
return a[i][j];
}
int x=maxsum(i+1,j,a);
int y=maxsum(i+1,j+1,a);
if(x<y) {
return a[i][j]+y;
}
return a[i][j]+x;
}
此时时间复杂度较高,因为存在较多的重复计算,我们为了避免重复计算可以事先定义好一个数组用来存放结果,调用此方法时如果已经得到了结果(及数组中有值),则直接return,就可以避免一些重复计算,避免重复计算的代码如下public int maxsum(int i,int j,int a[][],int max[][]) {
if(max[i][j]!=-1) {
return max[i][j];
}
if(i==a.length-1) {
max[i][j]=a[i][j];
return a[i][j];
}
int x=maxsum(i+1,j,a,max);
int y=maxsum(i+1,j+1,a,max);
if(x<y) {
max[i][j]=a[i][j]+y;
}
else max[i][j]=a[i][j]+x;
return max[i][j];
}
我们还可以将数学三角形问题用递推来解决用max数组里的最后一行元素和a数组里的元素算出max数组里的各个元素,具体实现代码如下public static void main(String[] args) {
int a[][]=new int [5][5];
a[0][0]=7;
a[1][0]=3; a[1][1]=8;
a[2][0]=8; a[2][1]=1; a[2][2]=0;
a[3][0]=2; a[3][1]=7; a[3][2]=4; a[3][3]=4;
a[4][0]=4; a[4][1]=5; a[4][2]=2; a[4][3]=6; a[4][4]=3;
int max[][]=new int[5][5];
for(int i=0;i<5;i++) {
max[4][i]=a[4][i];
}
for(int i=3;i>=0;i--) {
for(int j=0;j<i+1;j++) {
if(max[i+1][j]>max[i+1][j+1]) {
max[i][j]=max[i+1][j]+a[i][j];
}
else max[i][j]=max[i+1][j+1]+a[i][j];
}
}
System.out.println(max[0][0]);
}
递归到动归的一般转化方法:递归函数有n个参数,就定义一个n维的数组,数组的下标是递归函数的返回值,这样就可以从边界值开始,逐步填充数组,相当于计算递归函数值的逆过程。 比如这个问题我们在利用递归解决时必要参数是i和j,所以我们使用动归解题时是设一个二维数组max,从边界值开始逐步填充此数组。
2.无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态没有关系
max(k)的值,就是在k左边,终点数值小于ak,且长度最大的那个上升子序列的长度再加1。因为ak左边任何终点小于ak的子序列,加上ak后就能形成一个更长的上升子序列。 具体实现代码如下
public static void main(String[] args) {
int n=7;
int a[]= {1,7,3,5,9,4,8};
int max[]=new int[7];
max[0]=1; //max[0]肯定为1,先赋好值,再通过max[0]来递推后面的值
for(int i=1;i<n;i++) { //这个for循环表示以a【i】为终点
for(int j=0;j<i;j++) { //这个循环表示a【i】为终点时,a[i]之前的数
if(a[i]>a[j]) {
if(max[i]>max[j]+1) {
max[i]=max[i];
}
else max[i]=max[j]+1;
}
}
}
//将max数组里的数输出,max数组里的最大值就是所求的值
for(int i=0;i<max.length;i++) {
System.out.print(max[i]+" ");
}
}
递推公式
public static void main(String[] args) {
char str1[]="abcfbc".toCharArray();
char str2[]="abfcab".toCharArray();
int length1=str1.length+1;
int length2=str2.length+1;
int maxlen[][]=new int[length1][length2];
for(int i=0;i<length1;i++) {
maxlen[i][0]=0;
}
for(int j=0;j<length2;j++) {
maxlen[0][j]=0;
}
for(int i=1;i<length1;i++) {
for(int j=1;j<length2;j++) {
if(str1[i-1]==str2[j-1]) {
maxlen[i][j]=maxlen[i-1][j-1]+1;
}
else if(maxlen[i][j-1]>maxlen[i-1][j]) {
maxlen[i][j]=maxlen[i][j-1];
}
else {
maxlen[i][j]=maxlen[i-1][j];
}
}
}
for(int i=0;i<length1;i++) {
for(int j=0;j<length2;j++) {
System.out.print(maxlen[i][j]);
}
System.out.println();
}
}
问题:书写此代码时总是遇到错误原因是我们定义数组maxlen的大小时定义的太小,不能存放全部的结果。maxlen[i][j]表示第一个字符串的前i个字符和另一个字符串的前j个字符所能形成的最长公共子序列思考:假定数字串长度是n,添完加号后,表达式的最后一个加号加在第i个数字后面,那么整个表达式的最小值,就等于在前i个数字里插入m-1个加号所能形成的最小值,加上第i+1到第n个数字所能组成的数的值(i从1开始算)
if m=0 v(m,n)=n个数字组成的整数
else if (n<m+1 ) v(m,n)=∞
else v(m,n)=min{v(m-1,i)+Num(i+1,n)} (i=m......n-1)Num(i,j)表示从第i个数字到第j个数字所组成的数
我们还可以利用递归解决问题,现在是从n个物品中选出体积为40的组合,在确定第n个是要还是不要后我们可以将问题简化为从n-1个物品中选出体积为什么的组合,代码如下
public int jisuan(int k,int w,int a[]) {
if(w==0) return 1;
if(k<=0) return 0;
return jisuan(k-1,w,a)+jisuan(k-1,w-a[k-1],a);
}
public static void main(String[] args) {
int c[]= {0,20,20,20};
int max[][]=new int[4][41];
for(int i=0;i<41;i++) {
max[0][i]=0;
}
for(int i=0;i<4;i++) {
max[i][0]=1;
}
for(int i=1;i<=3;i++) {
for(int j=1;j<=40;j++) {
max[i][j]=max[i-1][j];
if(j-c[i]>=0) {
max[i][j]+=max[i-1][j-c[i]];
}
}
}
System.out.print(max[3][40]);
}
}
这个题中数组里的值表示有n个物品m容积中填满这m容积有多少选择,所以当其等于0时我们得到1;但是下一个问题中因为问题有多个需要满足的条件所以下一题时我们的数组表示在n个物品m容积背包情况下,价值和最大是多少。
public static void main(String[] args) {
int w[]= {0,20,20,20};
int d[]= {0,2,5,2};
int m=40;
int n=3;
int max[][]=new int[4][41];
for(int j=0;j<41;j++) {
if(j>=w[1]) max[1][j]=d[1];
else max[1][j]=0;
}
for(int i=1;i<=3;i++) {
for(int j=1;j<=40;j++) {
max[i][j]=max[i-1][j];
if(j-w[i]>=0) {
max[i][j]=Math.max(max[i-1][j], max[i-1][j-w[i]]+d[i]);
}
}
}
System.out.print(max[3][40]);
}
}
现需要把这些格子刷上保护漆。 你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!) 比如:a d b c e f 就是合格的刷漆顺序。 c e f d a b 是另一种合适的方案。 当已知 N 时,求总的方案数。当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模。 输入数据为一个正整数(不大于1000) 输出数据为一个正整数。 例如: 用户输入: 2 程序应该输出: 24