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

需要注意的一点:

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;
} 






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anyanyamy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值