需要注意的一点:
1.找出递推关系式,用空间存储中间结果,递推求解。
2.算法的描述中数组从1开始,实际上却是0开始,我处理的方式是不用0下标的元素,置为-1.
3.如有错误,欢迎指出~~
//动态规划 VS 分治
//Common: 均是分解问题,组合子问题求解
//Different:
/*
1.分治:自上而下 规划:自下而上
2.分治:不能避免重复计算、应尽量划分成单独部分
规划:可以避免重复,减少时空开销,分成有交集的部分,将公共部分结果保存,把中间结果存储起来以后使用
3.规划:解决优化问题,如旅行商;最优化原理:给出一个最优的决策序列,每个子序列必须是优的决策序列。
*/
//应用:Fab数列、求组合数(构造帕斯卡三角形)
//重点:最长公共子序列问题、矩阵链相乘、所有点对最短路径(Floyd)、背包问题
#include <stdio.h>
#define INF 32767
/*LCS
L[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;
else
L[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~n
D[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;
}


6346

被折叠的 条评论
为什么被折叠?



