动态规划

1、钢条切割


自顶向下

int maXt(int a, int b)
{
	if (a>b)
	{
		return a;
	}
	return b;
}
int cutRod(vector<int>vec, int n)
{
	if (n==0)
	{
		return 0;
	}
	/*if (memoziedArray[n-1]>0)
	{
		return memoziedArray[n - 1];
	}*/
	int max = 0;
	for (int i = 1; i <= n&&i <= vec.size();i++)
	{
		max = maXt(max, vec[i - 1] + cutRod(vec, n - i));
	}
	return max;
}

在测试时,我们发现,当钢条的长度稍微变大(比如n=30)时,上述程序的运行时间会大大增大。仔细考虑原因,会发现实际上我们做了很多重复的递归操作。比如在求解cutRod(p, n)过程中,我们会递归求解cutRod(p, 0 ~ n-1),而在求解cutRod(p, n-1)的过程中,同样我们会递归求解cutRod(p, 1 ~ n-1),可以看出,仅仅就是这两次的调用,就重复调用了n-2次。时间效率当然会下降。

改进:

int maXt(int a, int b)
{
	if (a>b)
	{
		return a;
	}
	return b;
}
int *memoziedArray;
int cutRod(vector<int>vec, int n)
{
	memoziedArray = memoziedArray == NULL ? new int[n] : memoziedArray;
	if (n==0)
	{
		return 0;
	}
	if (memoziedArray[n-1]>0)
	{
		return memoziedArray[n - 1];
	}
	int max = 0;
	for (int i = 1; i <= n&&i <= vec.size();i++)
	{
		max = maXt(max, vec[i - 1] + cutRod(vec, n - i));
	}

	memoziedArray[n - 1] = max;
	return max;
}
自底向上方法
int bottomUpCutRod(vector<int>vec, int n)
{
	int *memoziedArray = new int[n + 1];
	memoziedArray[0] = 0;
	int max = 0;
	for (int i = 1; i <= n;i++)
	{
		for (int j = 1; j <= i&&j <= vec.size();j++)
		{
			max = maXt(max, vec[j - 1] + memoziedArray[i-j]);
		}
		memoziedArray[i] = max;
	}
	return max;
}

2、最长公共子串问题LCS

     设序列X={x1,x2,……xm}和Y={y1,y2,……yn}的最长公共子序列为Z={z1,z2,……zk}。则有:

      (1)若xm=yn,则zk=xm=yn,且zk-1是Xm-1和Yn-1的最长公共子序列。

      (2)若xm!=yn且zk!=xm,则Z是Xm-1和Y的最长公共子序列。

      (3)若xm!=yn且zk!=yn,则Z是X和Yn-1的最长公共子序列。

      其中,Xm-1={x1,x2……xm-1},Yn-1={y1,y2……yn-1},Zk-1={z1,z2……zk-1}。

递推关系:用c[i][j]记录序列Xi和Yj的最长公共子序列的长度。其中,Xi={x1,x2……xi},Yj={y1,y2……yj}。当i=0或j=0时,空序列是xi和yj的最长公共子序列。此时,c[i][j]=0;当i,j>0,xi=yj时,c[i][j]=c[i-1][j-1]+1;当i,j>0,xi!=yj时,c[i][j]=max{c[i][j-1],c[i-1][j]}。

求最长公共子串的长度

void LCSLength(int m, int n, char *x, char *y, int **c, int **b)
{
	int i, j;

	for (i = 1; i <= m; i++)
		c[i][0] = 0;
	for (i = 1; i <= n; i++)
		c[0][i] = 0;

	for (i = 1; i <= m; i++)
	{
		for (j = 1; j <= n; j++)
		{
			if (x[i] == y[j])
			{
				c[i][j] = c[i - 1][j - 1] + 1;
				b[i][j] = 1;
			}
			else if (c[i - 1][j] >= c[i][j - 1])
			{
				c[i][j] = c[i - 1][j];
				b[i][j] = 2;
			}
			else
			{
				c[i][j] = c[i][j - 1];
				b[i][j] = 3;
			}
		}
	}
}

求公共子串

void LCS(int i, int j, char *x, int **b)
{
	if (i == 0 || j == 0)
	{
		return;
	}
	if (b[i][j] == 1)
	{
		LCS(i - 1, j - 1, x, b);
		cout << x[i] << " ";
	}
	else if (b[i][j] == 2)
	{
		LCS(i - 1, j, x, b);
	}
	else
	{
		LCS(i, j - 1, x, b);
	}
}

3、LSC改进:将数组b省去

void LCSLength(int m,int n,char *x,char *y,int **c)
{
	int i,j;

	for(i=1; i<=m; i++)
		c[i][0] = 0;
	for(i=1; i<=n; i++)
		c[0][i] = 0;

	for(i=1; i<=m; i++)
	{
		for(j=1; j<=n; j++)
		{
			if(x[i]==y[j])
			{
				c[i][j]=c[i-1][j-1]+1;
			}
			else if(c[i-1][j]>=c[i][j-1])
			{
				c[i][j]=c[i-1][j];
			}
			else
			{
				 c[i][j]=c[i][j-1];
			}
		}
	}
}

void LCS(int i,int j,char *x,int **c)
{
	if(i==0 || j==0)
	{
		return;
	}
	if(c[i][j]==c[i-1][j-1]+1)
	{
		LCS(i-1,j-1,x,c);
		cout<<x[i]<<" ";
	}
	else if(c[i-1][j]>=c[i][j-1])
	{
		LCS(i-1,j,x,c);
	}
	else
	{
		LCS(i,j-1,x,c);
	}
}

4、菲波那切数列

//避免重复运算的斐波那契数列运算  
int fib(int n, map<int, int>my_Map)
{
	if (n==0||n==1)
	{
		return 1;
	}
	else
	{
		map<int, int>::iterator iter = my_Map.find(n);
		if (iter==my_Map.end())
		{
			int temp = fib(n - 1, my_Map) + fib(n - 2, my_Map);
			my_Map.insert(pair<int, int>(n, temp));
			return temp;
		}
		else
		{
			return iter->second;
		}
	}
}

5、连续最长公共子串(多个)

string getLCSLength(string &s, string &t)  
{  
    int p = s.length();  
    int q = t.length();  
  
    string **num = new string *[p];    
    for(int i=0;i<p;i++)      
    {      
        num[i] = new string[q];    
    }     
  
    char char1 = '\0';  
    char char2 = '\0';  
  
    int len = 0;  
    string lcs = "" ;  
  
    for(int i=0; i<p; i++)  
    {  
        for (int j=0; j<t.length(); j++)  
        {  
            char1 = s.at(i);  
            char2 = t.at(j);  
            if(char1!=char2)  
            {  
                num[i][j] = "" ;  
            }  
            else  
            {  
                if (i==0||j==0)  
                {  
                    num[i][j]=char1;  
                }  
                else  
                {  
                    num[i][j]=num[i-1][j-1]+char1;  
                }  
                if(num[i][j].length()>len)  
                {  
                    len = num[i][j].length() ;  
                    lcs = num[i][j];  
                }  
                else if(num[i][j].length()==len)  
                {  
                    lcs = lcs + "," + num[i][j];  
                }  
            }  
        }  
    }  
  
    for(int i=0;i<p;i++)      
    {      
        delete[] num[i];   
    }   
    delete[] num;  
  
    return lcs;  
}  

6、最大子段问题

问题定义:对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。

int MaxSum(int n,int *a,int& besti,int& bestj)  
{     
    int sum = 0;  
    for(int i=0; i<n; i++)//控制求和起始项  
    {  
        for(int j=i; j<n; j++)//控制求和结束项  
        {  
            int thissum = 0;  
            for(int k=i; k<=j; k++)//求和  
            {  
                thissum += a[k];  
            }  
  
            if(thissum>sum)//求最大子段和  
            {  
                sum = thissum;  
                besti = i;  
                bestj = j;  
            }  
        }  
    }  
    return sum;  
}  

7、最大子段问题,改进

int MaxSum(int n,int *a,int& besti,int& bestj)  
{     
    int sum = 0;  
    for(int i=0; i<n; i++)//控制求和起始项  
    {  
        int thissum = 0;  
        for(int j=i; j<=n; j++)//控制求和结束项  
        {  
            thissum += a[j];//求和  
            if(thissum>sum)  
            {  
                sum = thissum;  
                besti = i;  
                bestj = j;  
            }  
              
        }  
    }  
    return sum;  
}

8、最大子段和:分治算法

int MaxSubSum(int *a,int left,int right)  
{     
    int sum = 0;  
    if(left == right)  
    {  
        sum = a[left]>0?a[left]:0;  
    }  
    else  
    {  
        int center = (left+right)/2;  
        int leftsum = MaxSubSum(a,left,center);  
        int rightsum = MaxSubSum(a,center+1,right);  
  
        int s1 = 0;  
        int lefts = 0;  
        for(int i=center; i>=left;i--)  
        {  
            lefts += a[i];  
            if(lefts>s1)  
            {  
                s1=lefts;  
            }  
        }  
  
        int s2 = 0;  
        int rights = 0;  
        for(int i=center+1; i<=right;i++)  
        {  
            rights += a[i];  
            if(rights>s2)  
            {  
                s2=rights;  
            }  
        }  
        sum = s1+s2;  
        if(sum<leftsum)  
        {  
            sum = leftsum;  
        }  
        if(sum<rightsum)  
        {  
            sum = rightsum;  
        }  
  
    }  
    return sum;  
}  
  
int MaxSum(int n,int *a)  
{  
    return MaxSubSum(a,0,n-1);  
}

9、最大子段和:动态规划

int MaxSum(int n,int *a)  
{  
    int sum=0,b=0;  
    for(int i=1; i<=n; i++)  
    {  
        if(b>0)  
        {  
            b+=a[i];  
        }  
        else  
        {  
            b=a[i];  
        }  
        if(b>sum)  
        {  
            sum = b;  
        }  
    }  
    return sum;  
}  

10、最大矩阵和

int MaxSum2(int m,int n,int a[M][N])  
{  
    int sum = 0;  
    int *b = new int[n+1];  
    for(int i=0; i<m; i++)//枚举行  
    {  
        for(int k=0; k<n;k++)  
        {  
            b[k]=0;  
        }  
  
        for(int j=i;j<m;j++)//枚举初始行i,结束行j  
        {  
            for(int k=0; k<n; k++)  
            {  
                b[k] += a[j][k];//b[k]为纵向列之和  
                int max = MaxSum(n,b);  
                if(max>sum)  
                {  
                    sum = max;  
                }  
            }  
        }  
    }  
    return sum;  
}  
  
int MaxSum(int n,int *a)  
{  
    int sum=0,b=0;  
    for(int i=1; i<=n; i++)  
    {  
        if(b>0)  
        {  
            b+=a[i];  
        }  
        else  
        {  
            b=a[i];  
        }  
        if(b>sum)  
        {  
            sum = b;  
        }  
    }  
    return sum;  
}  

11、最大m子段和

问题描述:给定由n个整数(可能为负数)组成的序列a1,a2,a3……an,以及一个正整数m,要求确定此序列的m个不相交子段的总和达到最大。最大子段和问题是最大m字段和问题当m=1时的特殊情形。

int MaxSum(int m,int n,int *a)  
{  
    if(n<m || m<1)  
        return 0;  
    int *b = new int[n+1];  
    int *c = new int[n+1];  
  
    b[0] = 0;//b数组记录第i行的最大i子段和  
    c[1] = 0;//c数组记录第i-1行的最大i-1子段和  
  
    for(int i=1; i<=m; i++)  
    {  
        b[i] = b[i-1] + a[i];  
        c[i-1] = b[i];  
        int max = b[i];  
  
        //n-m+i限制避免多余运算,当i=m时,j最大为n,可据此递推所有情形  
        for(int j=i+1; j<=i+n-m;j++)  
        {  
            b[j] = b[j-1]>c[j-1]?b[j-1]+a[j]:c[j-1]+a[j];  
            c[j-1] = max;//预先保存第j-1行的最大j-1子段和  
  
            if(max<b[j])  
            {  
                max = b[j];  
            }  
        }  
        c[i+n-m] = max;  
    }  
  
    int sum = 0;  
    for(int j=m; j<=n; j++)  
    {  
        if(sum<b[j])  
        {  
            sum = b[j];  
        }  
    }  
    return sum;  
}  

12、背包问题

给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值