南京邮电大学算法分析与设计实验二(动态规划法)

文章目录

    • 一、LCS问题
    • 二、矩阵连乘问题

一、LCS问题

//习题7-10-用备忘录算法求LCS
//备忘录方法是动态规划法的一个变种,它采用分治法思想,自顶向下直接递 归 求最优解。
//但与分治法不同的是,备忘录方法为每个已经计算的子问题建立备忘录,即保存子问题的计
//算结果以备需要时引用,从而避免子问题的重复求解。
//试改写当前程序的int LCS::LCSLength()函数,用备忘录方法来求解最长公共子序列。
//提示:备忘录方法采用的是递归求解方式,因此需要用一个公有成员函数int LCSLength()
//来调用私有递归成员函数int LCSLength(int i,int j); 共同实现。
#include "iostream"
#include "math.h"
using namespace std;

class LCS
{  
public:
     LCS(int nx, int ny, char *a, char *b);
	    ~LCS();
     int LCSLength();                 //求c和s数组,返回LCS的长度
     int LCSLength_Memo();            //备忘录算法求c和s数组,返回LCS长度,非递归函数
     void CLCS();                     //构造LCS,非递归
          
private:
     int LCSLength_Memo(int i,int j); //备忘录算法求c和s数组,返回LCS长度,递归函数
     void CLCS(int i, int j);         //构造LCS,递归
     int **c, **s, m, n;              // 算法中的二维数组c,二维数组s,字符串 x 和 y 的长度
     char *x, *y;                     // 要求最大公共子串的两个字符串 x 和 y
}; 

/// 
/// 构造函数
/// 初始化两个字符串的长度,地址;为c数组和s数组申请堆内存
/// 
/// 字符串 x 的长度
/// 字符串 y 的长度
/// 字符串 x 的地址
/// 字符串 y 的地址
LCS::LCS(int nx, int ny, char *a, char *b)
{  
    m=nx;    n=ny;
    x=a;     y=b;
    c=new int*[m+1];   s=new int*[m+1];
    for(int i=0; i<=m; i++)
    {   
        c[i]=new int [n+1];
        s[i]=new int [n+1];
    }
}

/// 
/// 析构函数
/// 释放c数组和s数组的内存
/// 
LCS::~LCS()
{  
    for(int i=0; i<=m; i++)		
    {  
        delete []c[i];   
        delete []s[i];
	}
    delete []c;   delete []s;
}

/// 
/// 求c和s数组,返回LCS的长度
/// 算法描述:
/// 1、初始化c数组:将c数组第0行和第0列除去c[0][0]赋初值0
/// 2、计算c数组:
///   2.1:如果x[i] == y[j],c[i][j] 等于其左上角的格子c[i-1][j-1]加1,s[i][j] = 1
///   2.2:否则,c[i][j] = Max{ c[i-1][j], c[i][j-1] } ,
///        即c[i][j]等于其左边和上边中值较大的那一个。
///        如果上边大,s[i][j] = 2;
///        否则左边大,s[i][j] = 3;
/// 3、返回c数组最后一个元素的值c[m][n],即公共子序列的长度
/// 
/// 
int LCS::LCSLength()
{
    for (int i = 1; i <= m; i++)
        c[i][0] = 0;
    for (int j = 1; j <= n; j++)
        c[0][j] = 0;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (x[i] == y[j])
            {
                c[i][j] = c[i - 1][j - 1] + 1;
                s[i][j] = 1;
            }
            else if (c[i - 1][j] >= c[i][j - 1])
            {
                c[i][j] = c[i - 1][j];
                s[i][j] = 2;
            }
            else
            {
                c[i][j] = c[i][j - 1];
                s[i][j] = 3;
            }
        }
    }
    return c[m][n];

}

/// 
/// 调用递归的重载函数 CLCS(int, int)
/// 
void LCS::CLCS()                     //构造LCS,非递归
{  CLCS(m,n);
}

/// 
/// 输出最大公共子序列
/// 算法分析:
/// 1、从 s 数组最后一格开始,按照向上或左上的方向查看s数组
/// 2、如果s[i][j] = 1,则输出x[i],并向左上方向移动
/// 3、如果s[i][j] = 2,则往上移动
/// 4、如果s[i][j] = 3,则往左移动
/// 5、直到i == 0 || j == 0 END
/// 
/// 
/// 
void LCS::CLCS(int i, int j)          //构造LCS,递归
{  
    if (i == 0 || j == 0)
        return;
    if (s[i][j] == 1){
        CLCS(i - 1, j - 1);
        cout << x[i];
    }
    else if (s[i][j] == 2)
        CLCS(i - 1, j);
    else
        CLCS(i, j - 1);


}                                     //O(m+n)

//备忘录算法求c和s数组,返回LCS长度
int LCS::LCSLength_Memo()
{ 	
    
    memset(c, INT_MAX, sizeof(c));
    return LCSLength_Memo(m, n);
}

/// 
/// 求解最大公共子序列的备忘录算法
/// 
/// 
/// 
/// 
int LCS::LCSLength_Memo(int i,int j)
{

    if (c[i][j] < INT_MAX)
        return c[i][j];

    if ((i == 0) || (j == 0))
        c[i][j] = 0;
    else if (x[i - 1] == y[j - 1])
        c[i][j] = LCSLength_Memo(i - 1, j - 1) + 1;
    else
    {
        int p = LCSLength_Memo(i - 1, j);
        int q = LCSLength_Memo(i, j - 1);
        if (p >= q)
            c[i][j] = p;
        else
            c[i][j] = q;
    }

    return c[i][j];

}


void main()
{
    int nx = 7, ny = 6, len1, len2;
    char x[] = "0abcbdab", y[] = "0bdcaba";

    LCS LL(nx, ny, x, y);

    cout << "x[]=\"0abcbdab\"; y[]=\"0bdcaba\"" << endl << endl;
    cout << "1. 不用备忘录算法求LCS的长度:" << endl;
    len1 = LL.LCSLength();                      //长度为4
    cout << "   LCS的长度=" << len1 << endl << endl;

    cout << "2. 用备忘录算法求LCS的长度:" << endl;
    len2 = LL.LCSLength_Memo();                 //长度为4
    cout << "   LCS的长度=" << len2 << endl << endl;
    cout << "3. LCS=\"";                        //LCS="bcba"
    LL.CLCS();
    cout << "\"" << endl;
}



二、矩阵连乘问题

//程序7-3-矩阵连乘算法和程序7-4-矩阵连乘算法的备忘录方法
#include "iostream"
#include "math.h"
#include "iomanip"    //输出格式头文件 
using namespace std;


//【程序7-3】  矩阵连乘算法
class MatrixChain
{  public:
	    MatrixChain(int mSize, int *q);	//创建二维数组m和s,一维数组p,并初始化
	    int MChain();						             //一般动态规划法求最优解值
	    int LookupChain();		           	//备忘录方法求最优解值(程序7-4)
	    void Traceback();			           	//构造最优解的公有函数
	    void Output();
   private:
	    void Traceback(int i, int j);		 //构造最优解的私有递归函数
	    int LookupChain(int i, int j);		//备忘录方法私有递归(程序7-4)
	    int *p, **m, **s, n;
};

MatrixChain::MatrixChain(int mSize, int *q)
{  n=mSize;
   m=new int*[n];   s=new int*[n];   p=new int[n+1];
   for(int i=0; i<n; i++)
   {  m[i]=new int [n];   m[i][i]=0;
      s[i]=new int [n];   s[i][i]=0;
	     p[i]=q[i];
   }
   p[n]=q[n];
}

int MatrixChain::MChain()
{
	for (int i = 0; i < n; i++) 
		m[i][i] = 0;
	for (int r = 2; r <= n; r++) {
		for (int i = 0; i <= n - r; i++) {
			int j = i + r - 1;
			m[i][j] = m[i + 1][j] + p[i] * p[i + 1] * p[j + 1];
			s[i][j] = i;
			for (int k = i + 1; k < j; k++) {
				int t = m[i][k] + m[k + 1][j] + p[i] * p[k + 1] * p[j + 1];
				if (t < m[i][j]) {
					m[i][j] = t;
					s[i][j] = k;
				}
			}
		}
	}
	return m[0][n - 1];
}

void MatrixChain::Traceback(int i, int j)
{
	if (i == j) {
		cout << "A" << i;
		return;
	}

	if (i < s[i][j]) cout << "(";
	Traceback(i, s[i][j]);
	if (i < s[i][j]) cout << ")";

	if (s[i][j] + 1 < j) cout << "(";
	Traceback(s[i][j] + 1, j);
	if (s[i][j] + 1 < j) cout << ")";

}

void MatrixChain::Traceback()
{ 
	cout << "(";
	Traceback(0, n - 1);
	cout << ")";
	cout << endl;

}
 
//【程序7-4】  矩阵连乘的备忘录方法
int MatrixChain::LookupChain(int i, int j)
{ 
	if (m[i][j] > 0)
		return m[i][j];
	if (i == j)
		return 0;
	int u = LookupChain(i + 1, j) + p[i] * p[i + 1] * p[j + 1];
	s[i][j] = i;
	for (int k = i + 1; k < j; k++) {
		int t = LookupChain(i, k) + LookupChain(k + 1, j) + p[i] * p[k + 1] * p[j + 1];
		if (t < u) {
			u = t;
			s[i][j] = k;
		}
		
	}	
	m[i][j] = u;
	return u;


}

int MatrixChain::LookupChain()
{ 

	return LookupChain(0, n - 1);
}

void MatrixChain::Output()
{  int i,j;
  
   cout<<"  m="<<endl;
   cout<<"  ";
   for(j=0; j<n; j++)
				  if(j<2) cout<<setw(4)<<j;
						else cout<<setw(6)<<j;
   cout<<endl;

   for(i=0; i<n; i++)
   {  cout<<"  "<<i<<" ";
      for(j=0; j<n; j++)
						{  if(i<j) cout<<setw(6)<<m[i][j];  //setw(6), 指定输出域宽为6
         else if(i==j) cout<<setw(2)<<m[i][j];
	        else cout<<setw(6)<<" ";
						}
	     cout<<endl;
   }

   cout<<"  s="<<endl;
   cout<<"    ";
   for(j=0; j<n; j++) cout<<j<<" ";
   cout<<endl;
   for(i=0; i<n; i++)
   {  cout<<"  "<<i<<" ";
      for(j=0; j<n; j++)
	  {  if(i<=j) cout<<s[i][j]<<" ";
	     else cout<<"  ";
	  }
	  cout<<endl;
   }
}

//例7-5: 6个矩阵连乘积A0*A1*A2*A3*A4*A5
//       A0:30*35, A1:35*15, A2:15*5, A3:5*10, A4:10:20, A5:20*25
//p={p0,p1,p2,p3,p4,p5,p6}={30,35,15,5,10,20,25}
void main()
{ int nn=6,k;
  int pp[7]={30,35,15,5,10,20,25};
  MatrixChain mm(nn,pp);
  
		cout<<"数据来源例7-5"<<endl;
  cout<<endl<<"1.不用备忘录方法求矩阵连乘的数乘最少次数"<<endl;
  k=mm.MChain();    cout<<"  最少数乘次数k="<<k<<endl; //最少数乘次数k=15125
  mm.Traceback();                                      //矩阵连乘次序: ( (A0(A1A2)) ((A3A4)A5) )
  cout<<endl;
  mm.Output();

//下面是备忘录方法
  cout<<endl<<"2.用备忘录方法求矩阵连乘的数乘最少次数"<<endl;
  MatrixChain a(nn,pp);
  k=a.LookupChain();   cout<<"  最少数乘次数k="<<k<<endl;
  a.Traceback();
  cout<<endl;
  a.Output();
}


Thanks♪(・ω・)ノ

你可能感兴趣的:(南邮实验报告,算法,动态规划)