动态规划学习

一:使用动态规划的条件:
1:原问题存在最优子结构。
2:原问题可划分为重叠子问题。
如果问题的一个最优解中包含了子问题的最优解,则该问题具有最优子结构。适用动态规划求解的最优化问题必须具有子问题的空间要“更小”,也就是用来求解原问题的递归算法可以反复地解同样的子问题,而不是总在产生新的子问题。动态规划算法总是充分利用重叠子问题,即通过每个子问题之求解一次,把解保存在一个可以查看的表中,而每次查表的时间为常数。
例如:
1. 数塔问题:要求找到从第1层到第N层的一条路径,该路径上的数字和最大。设P(n)就是这样一条路径。则在该路径上从第2层到第N层的这条路径必定是第二层到第N层数字之和最大的路径。

2. 装配线调度问题(算法导论P192):f1(i)表示完成装配线1上的第i站所选要的最短时间。
则f1(i) = min(f2(i-1)+ti+a1i,f1(i-1)+a1i);假设f1(i-1)<f2(i-1)+ti,则f1(i) = f1(i-1).此时f1(i-1)必定是装配线1上完成第i-1站所学要的最短时间。如果f1(i-1)>f2(i-1)+ti则f2(i-1)必定是装配线2上完成第i-1个站所需要的最短时间。(反证法)

3. 矩阵乘法链问题(算法导论P197):m[i,j]表示矩阵Ai*Ai+1*Ai+2*....*Aj所需要的最少乘法次数。
则m[i,j] = min(m[i,k]+m[k+1,j]+pi-1*pk*pj)(i=<k<j);在当前的k值下,m[i,k]必定是矩阵Ai*Ai+1*Ai+2*...Am所需要的最少乘法次数。m[k+1,j]必定是矩阵Ak+1*Ak+2+1*Ak+3*...Aj所需要的最少乘法次数。(反证法)

4:LCS(最长公共字串,算法导论P208):字符串1为x1x2x3x4...xm,字符串2为y1y2y3y4...yn,二者的LCS为z1z2z3z4...zk;设c[i,j]表示x1x2x3...xi和y1y2y3..yj的LCS长度,则 当xi==yj时c[i,j] = c[i-1,j-1]+1;且c[i-1,j-1]必定为x1x2x3...xi-1和y1y2y3..yj-1的LCS长度。当xi!=yj时,c[i,j] = max(c[i-1,j],c[i,j-1]);

5:0/1背包问题(数据结构):f(i,y)表示剩余物品为xixi+1xi+2...xn,剩余重量为y时的最大价值。则当y<wi时,f(i,y) = f(i+1,y);此时f(i+1,y)必定为剩余物品为xi+1xi+2...xn,剩余重量为y时的最大价值。当y>wi时,f(i,y) = max(f(i+1,y),f(i+1,y-wi)+pi),如果f(i+1,y)>f(i+1,y-wi)+pi),此时f(i+1,y)必定为剩余物品为xixi+1xi+2...xn,剩余重量为y时的最大价值。(反证法)如果f(i+1,y)<f(i+1,y-wi)+pi),f(i+1,y-wi)+pi)必定为剩余物品为xi+1xi+2...xn,剩余重量为y-wi时的最大价值。(反证法)

6:求解有向图所有点对的最短路径(数据结构):c[i,j,k]表示从点i到点j,中间所途径的点的最大值小于等于k时的最短路径。则当该路径中不包含点k时c[i,j,k] = c[i,j,k-1];当该路径包含k时,c[i,j,k] = c[i,k,k]+c[k,j,k].此时c[i,k,k]必定为从点i到点k,中间所途径的点的最大值小于等于k时的最短路径。该部分和矩阵乘法链差不多。

7: 最长递增子序列(算法导论习题):

二:动态规划最优子结构在问题域中以两种方式变化:
1)有多少个子问题被使用在原问题的一个最优解中。
2)在决定一个最优解中使用那些子问题时有多少个选择。
3)使用判断条件后,在划分子问题。(个人总结)

在装配线调度问题中,一个最优解使用了一个子问题,但是为了确定一个最优解。我们必须考虑两种选择。在矩阵乘法链中子链Ai*Ai+1*Ai+2*....*Aj的求解被分成连个子问题。Ai*Ai+1*Ai+2*...Am和Ak+1*Ak+2+1*Ak+3*...Aj,但存在着j-i中选择。对于0/1背包,最长公共字串,有向图的所有点对最短路径都是先使用判断条件。然后再划分后的子问题中做出选择。
三:动态规划于与贪婪法区别
贪心算法适用的问题也具有最优子结构。他与动态规划算法有一个明显的区别就是在贪心算法中是以自顶向下的方式适用最优子结构,贪心算法会先做选择,在当时看来是最优的。然后求解一个结果的子问题,而不是先寻找子问题的最优解然后再做选择。动态规划使用自底向上的方式构建最优解。在数塔问题中欲求解第一层到第N层,就必须先求解第2层到第N层。所以我们从N-1层逐步向上求解。对于装备线调度问题我们欲求解f1(n)就必须先求解f1(n-1),所以我们从f1(1)自低向上逐步求解。在矩阵乘法链中我们欲求解m[1,n]必须先求解m[1,k]和m[k+1,n]。整个的求解顺序是计算矩阵的上三角矩阵。但是是按对角线的次序,即先求解主对角线上的元素(为零)在向上逐层求解。在最长公共字串问题中。我们欲求解c[m,n]就必须求解c[m-1,n-1]或者是才c[m-1,n]和c[m,n-1],所以我们从c[1,1]逐渐向上求解。
四:源码
1.数塔问题
/************************************************************************/
/* 在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:
有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
已经告诉你了,这是个DP的题目,你能AC吗? 
输入
输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。
输出
对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。
样例输入
1
5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5
样例输出
30
                                                                     */
/************************************************************************/
#include "stdafx.h"
/* 数塔问题:已经描述过(常用算法-动态规划)                                                                     */
/************************************************************************/
#include "stdafx.h"

int GestMaxSumFromeShuTa(int pArray[][5],int levelNum)
{
	int resutlt = 0;
	int** sumArray = new int*[levelNum];
    for(int i=0;i<levelNum;i++)
		sumArray[i] = new int[levelNum];
	for(int i=0;i<levelNum;i++)
		sumArray[levelNum-1][i] = pArray[levelNum-1][i];
    for(int i=levelNum-2;i>=0;i--)
	   for(int j=0;j<=i;j++)
	   {
		   sumArray[i][j] = max(pArray[i][j]+sumArray[i+1][j],pArray[i][j]+sumArray[i+1][j+1]);
		   printf("(%d,%d),%d\n",i,j,sumArray[i][j]);
	   }
           

    resutlt = sumArray[0][0];
	for(int i=0;i<levelNum;i++)
		delete[] sumArray[i];
	delete[] sumArray;
	return resutlt;
}
void TestShuta()
{
	int Ta[5][5] = {{7},
	              {3,8},
	              {8,1,0},
	              {2,7,4,4},
	              {4,5,2,6,5}};
   printf("%d\n",GestMaxSumFromeShuTa(Ta,5));
}
2:装配线调度

/************************************************************************/
/* 装配线调度问题:算法导论 P193                                                                     */
/************************************************************************/

#include "stdafx.h"
#include <memory.h>

void BestSchedu(int* s1,int* s2,int len,int* t1,int* t2,int e1,int e2,int x1,int x2,int* resultArray,int& resultValue)
{
	/*s1装配线1在各个装配站的耗时,len装配站个数,t1又装配站1到装配站2耗时只能从装配站的j-1站到转配线2的j站,e1进入装配站
	1的时间,x1离开装配站1的时间,resultArray 线路数组,resultvalue返回最小耗时
	*/
	int* f1 = new int[len];
	memset(f1,0,sizeof(int)*len);
	int* f2 = new int[len];
    memset(f2,0,sizeof(int)*len);
	int* path1 = new int[len];
	memset(path1,0,sizeof(int)*len);
	int* path2 = new int[len];
	memset(path2,0,sizeof(int)*len);
    int lastStep = 0;
	f1[0] = *s1+e1;
	f2[0] = *s2+e2;

	for(int j=1;j<len;j++)
	{
		if(f1[j-1]<f2[j-1]+t2[j-1])
		{
			f1[j] = f1[j-1]+*(s1+j);
			path1[j] = 1;
		}
		else
		{
			f1[j] = f2[j-1]+t2[j-1]+*(s1+j);
			path1[j] = 2;
		}
		if(f2[j-1]<f1[j-1]+t1[j-1])
		{
			path2[j] = 2;
			f2[j] = f2[j-1]+*(s2+j);
		}
		else
		{
		    f2[j] = f1[j-1]+t1[j-1]+*(s2+j);
			path2[j] = 1;
		}
		
	}
   
	if(f1[len-1]+x1<f2[len-1]+x2)
	{
		resultValue = f1[len-1]+x1;
		lastStep =1;
	}
	else
	{
		resultValue = f2[len-1]+x2;
		lastStep = 2;
	}
    
    resultArray[len -1] = lastStep;
	for(int j=len-2;j>=0;j--)
	{
		if(lastStep==1)
			resultArray[j] = path1[j+1];
		else 
			resultArray[j] = path2[j+1];

		lastStep = resultArray[j];
	}
	delete f1;
	delete f2;
	delete path1;
	delete path2;

}
void TestBestSchedu()
{
	int s1[] = {7,9,3,4,8,4};
	int s2[] = {8,5,6,4,5,7};
	int t1[] = {2,3,1,3,4};
	int t2[] = {2,1,2,2,1};
	int e1 =2,e2 = 4;
	int x1 = 3,x2 = 2;

	int* resultArray = new int[6];
	int resultValue = 0;
	BestSchedu(s1,s2,6,t1,t2,e1,e2,x1,x2,resultArray,resultValue);
	printf("%d\n",resultValue);
	delete resultArray;
}

3:矩阵乘法链

/************************************************************************/
/* 矩阵乘法链:算法导论P198
A:pxq B:qxr
A*B 所用乘法次数为p*q*r
目的:求解一组加权括号使矩阵乘法链的结果所用乘法次数最少
最优子结构:m[i,j]为从矩阵AiAi+1....Aj加权括号
重叠子问题:m[i,j] = min{m[i,k]+m[k+1,j]+pi-1*pk*pj}*/
/************************************************************************/
#include "stdafx.h"
#include <memory.h>
int matrixchain(int* pArray,int** s,int len)
{
	int result=0;

	

	int** mArray = new int*[len+1];
		for(int i=0;i<=len;i++)
		{
			mArray[i] = new int[len+1];
			memset(mArray[i],0,sizeof(int)*(len+1));
		}

    for(int i=1;i<=len;i++)
		mArray[i][i] = 0;

	for(int k=1;k<=len;k++)
		for(int i=1;i<=len-k;i++)
		{
			int j= i+k;
			mArray[i][j] = 100000;
			for(int m=i;m<j;m++)
			{
				int temp = mArray[i][m]+mArray[m+1][j]+pArray[i-1]*pArray[m]*pArray[j];
				if(temp<mArray[i][j])
				{
					mArray[i][j] = temp;
					s[i][j] = m; 
				}
			}
		}

	result = mArray[1][len];
    for(int i=0;i<=len;i++)
	{
		delete[] mArray[i];
		mArray[i] = NULL;
	}

	delete mArray;
	mArray = NULL;
    return result;
}
void printResult(int** s,int i,int j)
{
	if(i==j)
		printf("A%d",i);
	else
	{
		printf("(");
		printResult(s,i,s[i][j]);
		printResult(s,s[i][j]+1,j);
		printf(")");
	}
}
void Testmatrixchain()
{
	int data[] = {30,35,15,5,10,20,25};
	int len =6;
	int** s;
	s= new int*[len+1];
	for(int i=0;i<len+1;i++)
		s[i] = new int[len+1];
	printf("%d\n",matrixchain(data,s,6));
    printResult(s,1,6);
	for(int i=0;i<len+1;i++)
		delete[] s[i];
	delete s;
}


 

4:求解最长公共字串

/************************************************************************/
/* 求解最长公共子序列:LCS  算法导论:p208
最优子结构:c[i,j]表示
字符串x; x1x2x3x4....xi
字符串y: y1y2y3y4....yj
最长公共字串的长度
重叠子问题:
c[i,j] = c[i-1,j-1]  x[i]==y[j];
c[i,j] = max(c[i-1,j],c[i,j-1])   x[i]\=y[j];
*/
/************************************************************************/
#include "stdafx.h"
int Lcs(char* pSrcA,int lenA,char* pSrcB,int lenB,int** pRecordArray,char** recordDir)
{
	//int result = 0;
	for(int i=0;i<=lenB;i++)
	{
		pRecordArray[0][i] = 0;
		recordDir[0][i] = '0';
	}
	for(int j=0;j<=lenA;j++)
	{
		pRecordArray[j][0] = 0;
		recordDir[j][0] = '0';
	}

	for(int i=1;i<=lenA;i++)
		for(int j=1;j<=lenB;j++)
		{
			if(pSrcA[i-1]==pSrcB[j-1])
			{
				pRecordArray[i][j] = pRecordArray[i-1][j-1]+1;
				recordDir[i][j] = 'p';
			}
			else
			{
				if(pRecordArray[i-1][j]>pRecordArray[i][j-1])
				{
					pRecordArray[i][j] = pRecordArray[i-1][j];
					recordDir[i][j] = 'q';
				}
				else
				{
					pRecordArray[i][j] = pRecordArray[i][j-1];
					recordDir[i][j] = 'r';
				}
			}
		}
		return pRecordArray[lenA][lenB];
}
void printLcs(char* pSrcA,char* pSrcB,char** recordDir,int i,int j)
{
	if(i<0||j<0)
		return;
	if(recordDir[i][j]=='p')
	{
		printf("%c",pSrcA[i-1]);
		printLcs(pSrcA,pSrcB,recordDir,i-1,j-1);
	}
	else if(recordDir[i][j]=='q')
		printLcs(pSrcA,pSrcB,recordDir,i-1,j);
	else
		printLcs(pSrcA,pSrcB,recordDir,i,j-1);

}
void testLcs()
{
	int lenA = 8;
	int lenB = 7;

	char x[]={'A','B','C','B','D','A','B','E'};
	char y[]={'B','D','C','A','B','A','E'};
	//int result[9][8];
	//char record[9][8];
	int** result = new int*[lenA+1];
	for(int i=0;i<=lenA;i++)
		result[i] = new int[lenB+1];

	char** record = new char*[lenA+1];
	for(int i=0;i<=lenA;i++)
		record[i] = new char[lenB+1];


	printf("%d\n",	Lcs(x,8,y,7,result,record));
    for(int i=0;i<=lenA;i++)
	{
		for(int j=0;j<=lenB;j++)
			printf("%c ",record[i][j]);
		printf("\n");
	}
    printLcs(x,y,record,lenA,lenB);
	for(int i=0;i<=lenA;i++)
		delete[] record[i];
	delete[] record;
	for(int i=0;i<=lenA;i++)
		delete[] result[i];
	delete[] result;
    
}

/************************************************************************/

5:求解所有点对最短路径    动态规划+BFS
/* 动态规划求解所有节点最短路径对 ,时间复杂性为0(n.^3),Dijkstra算法求解的是单源最短路径
时间复杂性为0(n.^2)*/
/************************************************************************/

#include "stdafx.h"
void printPath(int keyArray[][6],int num,int i,int j)
{
	if(i==j)
		return;
	if(keyArray[i][j]==0)
		printf("%d ",j);
	else
	{
		printPath(keyArray,num,i,keyArray[i][j]);
		printPath(keyArray,num,keyArray[i][j],j);
	}
}
void GetAllPointShortestPath(int pAdj[][6],int num,int recordArray[][6],int keyArray[][6])
{
	for(int i=1;i<=num;i++)
		for(int j=1;j<=num;j++)
		{
			recordArray[i][j] = pAdj[i][j];
			keyArray[i][j]= 0;
		}
	//for(int i=1;i<=num;i++)
	//	recordArray[i][i] = 0;

	
		for(int i=1;i<=num;i++)
			for(int j=1;j<=num;j++)
				for(int k=1;k<=num;k++)
			       {
				           if(recordArray[i][k]!=-1&&recordArray[k][j]!=-1&&(recordArray[i][j]==-1||
					          recordArray[i][j]>recordArray[i][k]+recordArray[k][j]))
				        {
					            recordArray[i][j] = recordArray[i][k]+recordArray[k][j];
					            keyArray[i][j] = k;
				         }
			        }
   
	/*for(int i=1;i<=num;i++)
		for(int j=1;j<=num;j++)
			printf("%d ",keyArray[i][j]);*/
    for(int i=1;i<=num;i++)
		for(int j=1;j<=num;j++)
		{
			/*if(recordArray[i][j]>0)
			{
				printf("from %d to %d:shortest path is %d\n",i,j,recordArray[i][j]);
				printf("the path is: \n");
				printPath(keyArray,num,i,j);
				printf("\n");
			}*/
			if(recordArray[i][j]==-1)
				printf("there is no way from %d to %d\n",i,j);
			else
			{
				printf("the path from %d to %d is:%d\n",i,j,recordArray[i][j]);
				printf("%d ",i);
				printPath(keyArray,num,i,j);

				printf("\n");
			}

		}
}
void testShortestPath()
{
	int array[6][6]={{0,0,0,0,0,0},
	{0,-1,4,2,-1,8},
	{0,-1,-1,-1,4,5},
	{0,-1,-1,-1,1,-1},
	{0,-1,-1,-1,-1,3},
	{0,-1,-1,-1,-1,-1}};
	int cost[6][6];
	int key[6][6];
	GetAllPointShortestPath(array,5,cost,key);
}

6:求解最长递增子序列

#include "stdafx.h"
/************************************************************************/
/* 求解最长递增子序列                                                                     */
/************************************************************************/
int FindPosition(int dst,int* s,int s_len)
{
	for(int i=1;i<=s_len;i++)
		if(s[i]>dst)
			return i;
	return s_len+1;
}
int FindPosition_binary(int dst,int* s,int s_len)
{
	int low=1,high = s_len,mid = 0;
	//bool find = false;
	while(low<=high)
	{
	    mid = (low+high)/2;
		
		if(s[mid]>=dst)
			high = mid - 1;
		else
			low = mid +1 ;
	}
	if(s[mid]<dst)
		return mid + 1;
	else
		return mid;
}
int longestIncreaseSub(int* pSrc,int len)
{
	int result = 0,s_len = 0;
	int* tempSub = new int[len];
	int* s = new int[len+1];
    

	tempSub[0] = 1;
	s[1] =  pSrc[0];
	s_len = 1;
    result = 1;
	for(int i=1;i<len;i++)
	{
		int iPosition = FindPosition_binary(pSrc[i],s,s_len);
		if(iPosition>s_len)
		{
			s[iPosition] = pSrc[i];
			s_len++;
		}
		else
			s[iPosition] = pSrc[i];
        tempSub[i] = iPosition;
		if(tempSub[i]>result)
			result = tempSub[i];
	}
    
	for(int i=0;i<len;i++)
		printf("%d ",tempSub[i]);
	printf("\n");

	for(int i=1;i<=s_len;i++)
		printf("%d ",s[i]);
	printf("\n");
	delete[] s;
	delete[] tempSub;
	return result;
}
void testLongestIncreaseSub()
{
	int a[]={1,3,4,2,7,8,6,3,4,5,6,7};
	printf("%d\n",longestIncreaseSub(a,12));
}

 

7:最大子段求和

/************************************************************************/
/*  给定由n个整数(可能为负整数)组成的序列a1,a2,...,an,给出动态规划算法求该序列形如(ai+...+aj)
的子段的和的最大值(1<=i<=j<=n),并说明算法的时间复杂度和空间复杂度。当所有整数都为负整数时定义其最大字段和为0.
依此定义,所求的最优值为max(0,max(ai+...+aj))(1<=i<=j<=n)。例如,当(a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2)时,
最大子段和为(a2+a3+a4)=20                                                                   */
/************************************************************************/

/************************************************************************/
/* 最大子段字段和问题:已练习过(常用算法-动态规划)                                                                     */
/************************************************************************/
#include "stdafx.h"
int SubSumMax(int* pArray,int len)
{
	int* tempArray = new int[len];
	int max=tempArray[0] = pArray[0];
	for(int i=1;i<len;i++)
	{
		if(tempArray[i-1]<0)
			tempArray[i] = pArray[i];
	    else
			tempArray[i] = pArray[i]+tempArray[i-1];
		if(tempArray[i]>max)
			max = tempArray[i];
	}
	delete[] tempArray;
	return max;
}
void testSubSumMax()
{
	int testData[] = {7,3,-17,9,3,34,-5,10};
	printf("%d\n",SubSumMax(testData,8));
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值