算法设计与分析复习-动态规划例子代码

来源:互联网 发布:yoosee软件下载 编辑:程序博客网 时间:2024/04/29 19:50

需要注意的一点:

1.找出递推关系式,用空间存储中间结果,递推求解。

2.算法的描述中数组从1开始,实际上却是0开始,我处理的方式是不用0下标的元素,置为-1.

3.如有错误,欢迎指出~~


//动态规划 VS 分治 //Common: 均是分解问题,组合子问题求解//Different:/*1.分治:自上而下  规划:自下而上 2.分治:不能避免重复计算、应尽量划分成单独部分  规划:可以避免重复,减少时空开销,分成有交集的部分,将公共部分结果保存,把中间结果存储起来以后使用 3.规划:解决优化问题,如旅行商;最优化原理:给出一个最优的决策序列,每个子序列必须是优的决策序列。 */ //应用:Fab数列、求组合数(构造帕斯卡三角形)//重点:最长公共子序列问题、矩阵链相乘、所有点对最短路径(Floyd)、背包问题#include <stdio.h>#define INF 32767/*LCSL[i,j]表示 a1~i b1~j的LCS的长度,若i=0或j = 0,L[i,j] = 0若ai = bj,L[i,j] = L[i-1,j-1] +1若ai!= bj,L[i,j] = max{L[i,j-1],L[i-1,j]}采用(n+1) * (m+1)的表存储长度。 时间:O(M*N)  空间:O(min{m,n}) */ //n,m是串的长度,输入的串第一个元素无效 int LCS(char A[],int n,char B[],int m){printf("\n\nLCS:\n");int i,j;int L[n+1][m+1]; for(i = 0;i<=n;i++)L[i][0] = 0;for(j = 0; j<=m;j++)L[0][j] = 0;for(i = 1;i<=n;i++){for(j = 1;j<=m;j++){if(A[i] == B[j])L[i][j] = L[i-1][j-1] + 1;elseL[i][j] = ((L[i-1][j] > L[i][j-1])? L[i-1][j] : L[i][j-1]);printf("%3d",L[i][j]);}printf("\n");} return L[n][m];}//矩阵链相乘/*用n+1维数:r1,r2...r(n+1)ri,r(i+1)表示:Mi的行数和列数。 M[i][j]表示:  M[i]*M[i+1]*...*M[j]的乘积 C[i][j]表示: 乘法次数总耗费C[i,j] = min{C[i][k] + C[k][j] + ri*rk*r(j+1)}  1<k<=j  n阶矩阵:画一个n*n的上三角形表,对角线C[i,j]=0表示无乘法耗费,然后依次往右上角填充。 时间: O(N^3)  空间:O(N^2) */ //输入:n个矩阵的链的维数对应于r[1~n+1],前n个为行数,r[n+1]为最后一个矩阵Mn的列数 //例如 METCHAIN(a,6)  不用下标为0的矩阵元素 int METCHAIN(int r[],int n){printf("\n\nMETCHAIN:\n");int i,j,d,k,C[n+1][n+1];for(i = 1; i <= n; i++)C[i][i] = 0;//填充对角线d1 ~ d(n-1) for(d = 1;d <= n-1; d++){//填充对角线di for(i = 1;i <= n-d; i++){j = i + d;C[i][j] = INF;for(k = i + 1; k <= j ; k ++)C[i][j] = (C[i][j] < C[i][k-1]+C[k][j]+r[i]*r[k]*r[j+1]? C[i][j] : C[i][k-1]+C[k][j]+r[i]*r[k]*r[j+1]);printf("C[%d %d]:%3d  ",i,j,C[i][j]);}printf("\n");}return C[1][n];} //Floyd算法/*n个点的有向图,用D[k][i][j]表示顶点i到j不经过编号大于k的任何顶点的最短路径长度。  k:0~nD[k][i][j] = MIN{D[k-1][i][j], D[k-1][i][K] + D[k-1][K][j]}输入:n*n矩阵 L[1~n][1~n] 元素为对应边长度 时间: O(N^3)  空间:O(N^2) */ //输入的L的0维未使用,使用的是1~n void Floyd(int L[][4],int n){printf("\n\nFloyd:\n");int D[n+1][n+1];for(int i = 1;i <= n; i++)for(int j = 1; j <=n;j++)D[i][j] = L[i][j];for(int k = 1; k <= n; k ++){printf("D(%d)--------------------------\n\n",k);for(int i = 1; i <=n; i++){for(int j = 1;j<= n; j++){D[i][j] = (D[i][j]< (D[i][k]+D[k][j])? D[i][j]: D[i][k] + D[k][j]); printf("%3d ",D[i][j]);}printf("\n");}}}//背包问题:/*U = {u1,u2,..un} 物品集合,对于1<=j<=n,  sj,vj分别为物品体积和价值 现在使总体积不超过C,总价值最大的方法。V[I][J] 表示 从前I项中取出装入体积J的背包的最大价值。  I:0~n J:0~C要求的是V[n][C], I,J有一个为0时,V即为0.  V[I][J]  = V[I-1][J]  J<si  //第I件物品装不下V[I][J]  = max{V[I-1][J],V[I-1][J-si] + vi}  I>0&& J>=si //可以选择是放I物品还是不放,根据价值来判断时间:0(N*C)  空间:O(C) */ int KNAPSACK(int s[],int v[],int n,int C){printf("\n\nKNAPSACK:\n");int i,j,V[n+1][C+1];for(i = 0; i <= n ; i++)V[i][0] = 0;for(j = 0; j <= C; j++)V[0][j] = 0;for(i = 1; i <= n; i++){for(j = 1;j <= C; j++){V[i][j] = V[i-1][j];//装得下第i件物品 if(s[i] <= j)V[i][j] = (V[i][j]>V[i-1][j-s[i]]+v[i]? V[i][j] : V[i-1][j-s[i]]+v[i]);printf("%3d ",V[i][j]);}printf("\n");}return V[n][C];}  int main(){char A[] = {' ','x','y','x','x','z','x','y','z','x','y'};char B[] = {' ','z','x','z','y','y','z','x','x','y','x','x','z'};//输入的数组下标为0的元素不用,可以直接填充为-1 int M[] = {-1,5,10,4,6,10,2}; int L[][4] = {-1,-1,-1,-1,-1, 0, 2, 9,  -1, 8, 0, 6,  -1, 1,INF,0};  int s[] = {-1,2,3,4,5};int v[] = {-1,3,4,5,7};int C = 9;//A,B的0下标元素未使用。 LCS(A,10,B,12);METCHAIN(M,5);Floyd(L,3);KNAPSACK(s,v,4,C);return 0;} 



0 0