码迷,mamicode.com
首页 > 编程语言 > 详细

《趣学算法》第四章 动态规划源代码

时间:2020-09-16 12:22:41      阅读:30      评论:0      收藏:0      [点我收藏+]

标签:相关   遍历   字符   不用   std   ade   max   规模   绝对值   

动态规划相关代码实现:

1、孩子有多像爸爸——最长的公共子序列

//program 4-1
#include <iostream>
#include<cstring>
using namespace std;
const int N=1002;
int c[N][N],b[N][N];
char s1[N],s2[N];
int len1,len2;

void LCSL()
{
    int i,j;
    for(i = 1;i <= len1;i++)//控制s1序列不同的子问题
      for(j = 1;j <= len2;j++)//控制s2序列不同的子问题
      {
        if(s1[i-1]==s2[j-1])
        {
            c[i][j] = c[i-1][j-1]+1;//如果当前字符相同,则公共子序列的长度为该字符前的最长公共序列+1
            b[i][j] = 1;
        }
        else
        {
             if(c[i][j-1]>=c[i-1][j])
            {
                c[i][j] = c[i][j-1];
                b[i][j] = 2;
            }
            else
            {
                c[i][j] = c[i-1][j];
                b[i][j] = 3;
            }
        }
     }
}

void print(int i, int j)//根据记录下来的信息构造最长公共子序列(从b[i][j]开始递推)
{
    if(i==0 || j==0) return;
    if(b[i][j]==1)
    {
        print(i-1,j-1);
        cout<<s1[i-1];
    }
    else if(b[i][j]==2)
            print(i,j-1);
         else
            print(i-1,j);
}

int main()
{
    int i,j;
    cout << "输入字符串s1:"<<endl;
    cin >> s1;
    cout << "输入字符串s2:"<<endl;
    cin >> s2;
    len1 = strlen(s1);//计算两个字符串的长度
    len2 = strlen(s2);
    for(i = 0;i <= len1;i++)
    {
        c[i][0]=0;//初始化第一列为0
    }
    for(j = 0;j<= len2;j++)
    {
        c[0][j]=0;//初始化第一行为0
    }
    LCSL();
    cout << "s1和s2的最长公共子序列长度是:"<<c[len1][len2]<<endl;
    cout << "s1和s2的最长公共子序列是:";
    print(len1,len2);
    cout<<endl;
    // /*用于测试
    cout<<"c[i][j]数组:"<<endl;
    for(i = 0;i <= len1;i++)
    {
        for(j = 0;j <= len2;j++)
          cout <<"  "<<c[i][j];
        cout<<endl;
    }
    cout<<"b[i][j]数组:"<<endl;
    for(i = 0;i <= len1;i++)
    {
        for(j = 0;j <= len2;j++)
          cout <<"  "<<b[i][j];
        cout<<endl;
    }
    // */用于测试
    return 0;
}

2、DNA基因鉴定——编辑距离

//program 4-2
#include <iostream>
#include <cstring>
using namespace std;
const int N=100;
char str1[N],str2[N];
int d[N][N]; //定义辅助数组,d[i][j]表示str1前i个字符和str2前j个字符的编辑距离。

int min(int a, int b)
{
    return a<b?a:b;//返回较小的值
}

int editdistance(char *str1, char *str2)
{
    int len1 = strlen(str1);
    int len2 = strlen(str2);//取字符串大小

    for(int i=0;i<=len1;i++)//当第二个串长度为0,编辑距离初始化为i
        d[i][0]= i;

    for(int j=0;j<=len2;j++)//当第一个串长度为0,编辑距离初始化为j
        d[0][j]=j;

    for(int i=1;i <=len1;i++)//遍历两个数组
    {
        for(int j=1;j<=len2;j++)
        {
            int diff;//判断str[i]是否等于str2[j],相等为0,代表不用改变str1,不相等为1,代表需要改变str1。
            if(str1[i-1] == str2[j-1])//相等
                diff = 0 ;
            else
                diff = 1 ;
            int temp = min(d[i-1][j] + 1, d[i][j-1] + 1);//先两者取最小值
            d[i][j] = min(temp, d[i-1][j-1] + diff);//再取最小值,相当于三者取最小值d[i-1][j] + 1, d[i][j-1] + 1,d[i-1][j-1] + diff
        }
    }
    return d[len1][len2];
}
int main()
{
    cout << "输入字符串str1:"<<endl;
    cin >> str1;
    cout << "输入字符串str2:"<<endl;
    cin >> str2;
    cout << str1<< "和"<<str2<<"的编辑距离是:"<<editdistance(str1,str2);
    return 0;
}

3、长江一日游——游艇租赁

//program 4-3
#include<iostream>
using namespace std;
const int ms = 1000;
int r[ms][ms],m[ms][ms],s[ms][ms];    //i到j站的租金
int n;            //共有n个站点
void rent()
{
    int i,j,k,d;
    for(d=3;d<=n;d++) //将问题分为小规模为d
    {
        for(i=1;i<=n-d+1;i++)
            {
                j=i+d-1;
                for(k=i+1;k<j;k++)  //记录每一个小规模内的最优解
                {
                    int temp;
                    temp=m[i][k]+m[k][j];
                    if(temp<m[i][j])
                        {
                           m[i][j]=temp;
                           s[i][j]=k;
                        }
                }
            }
    }
}
void print(int i,int j)
{
    if(s[i][j]==0 )
    {
       cout << "--"<<j;
       return ;
    }
    print(i,s[i][j]);
    print(s[i][j],j);
}
int main()
{
    int i,j;
    cout << "请输入站点的个数 n:";
    cin >> n;
    cout << "请依次输入各站点之间的租金:";
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;++j)
        {
            cin>>r[i][j];
            m[i][j]=r[i][j];
        }
    rent();
    cout << "花费的最少租金为:" <<m[1][n] << endl;
    cout <<"最少租金经过的站点:"<<1;
    print(1,n);
    // /*用于测试 输出m[][]和s[][]数组
    cout << endl;
    for (i = 1; i <= n; i++ )
    {
       for (j = 1; j <=2*(i-1); j++ )
          cout << "  " ;
       for (j=i; j<=n;j++ )
          cout << m[i][j]<<"   " ;
       cout << endl;
    }
    for (i=1; i<=n;i++ )
    {
         for (j =1; j <=2*(i-1);j++ )
           cout << "  " ;
         for (j=i; j<=n; j++ )
           cout << s[i][j]<<"   " ;
         cout << endl;
    }
    cout << endl;
    // /*/用于测试
    return 0;
}

4、快速计算——矩阵连乘

//program 4-4
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int msize = 100;
int p[msize];
int m[msize][msize];
int s[msize][msize];
int n;

void matrixchain()
{
    int i,j,r,k;
    memset(m,0,sizeof(m));
    memset(s,0,sizeof(s));
    for(r = 2; r <= n; r++)         //不同规模的子问题
    {
        for(i = 1; i <= n-r+1; i++)
        {
           j = i + r - 1;
           m[i][j] = m[i+1][j] + p[i-1] * p[i] * p[j];  //决策为k=i的乘法次数
           s[i][j] = i;                     //子问题的最优策略是i;
           for(k = i+1; k < j; k++) //对从i到j的所有决策,求最优值,记录最优策略
            {
                int t = m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j];
                if(t < m[i][j])
                {
                    m[i][j] = t;
                    s[i][j] = k;
                }
            }
        }
    }
}
void print(int i,int j)
{
    if( i == j )
    {
       cout <<"A[" << i << "]";
       return ;
    }
    cout << "(";
    print(i,s[i][j]);
    print(s[i][j]+1,j);
    cout << ")";
}
int main()
{
    cout << "请输入矩阵的个数 n:";
    cin >> n;
    int i ,j;
    cout << "请依次输入每个矩阵的行数和最后一个矩阵的列数:";
    for (i = 0; i <= n; i++ )
        cin >> p[i];
    matrixchain();
    print(1,n);
    cout << endl;
    /*用于测试
    for (i = 1; i <= n; i++ )
        {
          for (j = i; j <= n; j++ )
            cout << m[i][j]<<"  " ;
          cout << endl;
        }
     for (i = 1; i <= n; i++ )
        {
          for (j = i; j <= n; j++ )
            cout << s[i][j]<<"  " ;
          cout << endl;
        }
    cout << endl;
    */
    cout << "最小计算量的值为 " << m[1][n] << endl;
}

5、切呀切皮萨——最优三角剖分

//program 4-5
#include<iostream>
#include<sstream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std  ;
const int M= 1000 + 5 ;
int n ;
int s[M][M] ;
double m[M][M],g[M][M];
void  Convexpolygontriangulation()
{
    for(int i = 1 ;i <= n ; i++)    // 初始化
    {
        m[i][i] = 0 ;
        s[i][i] = 0 ;
    }
    for(int d = 2 ;d <= n ; d++)         // 枚举点的个数
      for(int i = 1 ;i <= n - d + 1 ; i++)  // 枚举起始点
      {
          int j = i + d - 1 ;         // 终点
          m[i][j] = m[i+1][j] + g[i-1][i] + g[i][j] + g[i-1][j] ;
          s[i][j] = i ;
          for(int k = i + 1 ;k < j ; k++)     // 枚举中间点
          {
              double temp = m[i][k] + m[k+1][j] + g[i-1][k] + g[k][j] + g[i-1][j] ;
              if(m[i][j] > temp)
              {
                  m[i][j] = temp ;   // 更新最优值
                  s[i][j] = k ;      // 记录中间点
              }
          }
      }
}
void print(int i , int j)  // 输出所有的弦
{
    if(i == j)  return ;
    if(s[i][j]>i)
      cout<<"{v"<<i-1<<"v"<<s[i][j]<<"}"<<endl;
    if(j>s[i][j]+1)
      cout<<"{v"<<s[i][j]<<"v"<<j<<"}"<<endl;
    print(i ,s[i][j]);
    print(s[i][j]+1 ,j);
    //cout<<"{ v"<<i-1<<" , v"<<s[i][j]<<" , v"<<j<<" }"<<endl; //输出所有剖分后的三角形
}
int main()
{
    int i,j;
    cout << "请输入顶点的个数 n:";
    cin >> n;
    n-- ;
    cout << "请依次输入各顶点的连接权值:";
    for(int i = 0 ;i <= n ; i++)   // 输入各个顶点之间的距离
        for(int j = 0 ;j <= n ; j++)
            cin>>g[i][j] ;
    Convexpolygontriangulation();
    cout<<m[1][n]<<endl;
    // /*用于测试 输出m[][]和s[][]数组
    for (i = 1; i <= n; i++ )
    {
       for (j = 1; j <=2*(i-1); j++ )
          cout << "  " ;
       for (j = i; j <= n; j++ )
          cout << m[i][j]<<"   " ;
       cout << endl;
    }
    for (i = 1; i <= n; i++ )
    {
         for (j = 1; j <=2*(i-1); j++ )
           cout << "  " ;
         for (j = i; j <= n; j++ )
           cout << s[i][j]<<"   " ;
         cout << endl;
    }
    cout << endl;
    // /*/用于测试
    print(1 ,n); // 打印路径
    return 0 ;
}

6、小石子游戏——石子合并

//program 4-6
#include <iostream>
#include <string>
using namespace std;

const int INF = 1 << 30;
const int N = 205;
int Min[N][N], Max[N][N];
int sum[N];
int a[N];
int min_Circular,max_Circular;

void straight(int a[],int n)
{
    for(int i=1;i<=n;i++)  // 初始化
        Min[i][i]=0, Max[i][i]=0;
    sum[0]=0;
    for(int i=1;i<=n;i++)
       sum[i]=sum[i-1]+a[i];
    for(int v=2; v<=n; v++) // 枚举合并的堆数规模
    {
        for(int i=1; i<=n-v+1; i++) //枚举起始点i
        {
            int j = i + v-1; //枚举终点j
            Min[i][j] = INF; //初始化为最大值
            Max[i][j] = -1; //初始化为-1
            int tmp = sum[j]-sum[i-1];//记录i...j之间的石子数之和
            for(int k=i; k<j; k++) {   //枚举中间分隔点
                Min[i][j] = min(Min[i][j], Min[i][k] + Min[k+1][j] + tmp);
                Max[i][j] = max(Max[i][j], Max[i][k] + Max[k+1][j] + tmp);
            }
        }
    }
}
void Circular(int a[],int n)
{
    for(int i=1;i<=n-1;i++)
        a[n+i]=a[i];
    n=2*n-1;
    straight(a, n);
    n=(n+1)/2;
    min_Circular=Min[1][n];
    max_Circular=Max[1][n];
    for(int i=2;i<=n;i++)
    {
        if(Min[i][n+i-1]<min_Circular)
           min_Circular=Min[i][n+i-1];
        if(Max[i][n+i-1]>max_Circular)
           max_Circular=Max[i][n+i-1];
    }
}

int main()
{
    int n;
    cout << "请输入石子的堆数 n:";
    cin >> n;
    cout << "请依次输入各堆的石子数:";
    for(int i=1;i<=n;i++)
       cin>>a[i];
    straight(a, n);
    cout<<"路边玩法(直线型)最小花费为:"<<Min[1][n]<<endl;
    cout<<"路边玩法(直线型)最大花费为:"<<Max[1][n]<<endl;
    Circular(a,n);
    cout<<"操场玩法(圆型)最小花费为:"<<min_Circular<<endl;
    cout<<"操场玩法(圆型)最大花费为:"<<max_Circular<<endl;
    return 0;
}

//program 4-6-1
#include <iostream>
#include <string>
using namespace std;

const int INF = 1 << 30;
const int N = 205;
int Min[N][N], Max[N][N],s[N][N];
int sum[N];
int a[N];
int min_Circular,max_Circular;

void get_Min(int n)
{
    for(int v=2; v<=n; v++) // 枚举合并的堆数规模
    {
        for(int i=1; i<=n-v+1; i++) //枚举起始点i
        {
            int j = i + v-1; //枚举终点j
            int tmp = sum[j]-sum[i-1];//记录i...j之间的石子数之和
            int i1=s[i][j-1]>i?s[i][j-1]:i;
            int j1=s[i+1][j]<j?s[i+1][j]:j;
            Min[i][j]=Min[i][i1]+Min[i1+1][j];
            s[i][j]=i1;
            for(int k=i1+1; k<=j1; k++) //枚举中间分隔点
                if(Min[i][k]+ Min[k+1][j]<Min[i][j])
                {
                    Min[i][j]=Min[i][k]+Min[k+1][j];
                    s[i][j]=k;
                }
            Min[i][j]+=tmp;
        }
    }
}

void get_Max(int n)
{
    for(int v=2; v<=n; v++) // 枚举合并的堆数规模
    {
        for(int i=1; i<=n-v+1; i++) //枚举起始点i
        {
            int j = i + v-1; //枚举终点j
            Max[i][j] = -1; //初始化为-1
            int tmp = sum[j]-sum[i-1];//记录i...j之间的石子数之和
            if(Max[i+1][j]>Max[i][j-1])
               Max[i][j]=Max[i+1][j]+tmp;
            else
               Max[i][j]=Max[i][j-1]+tmp;
        }
    }
}

void straight(int a[],int n)
{
    for(int i=1;i<=n;i++)  // 初始化
        Min[i][i]=0, Max[i][i]=0, s[i][i]=0;
    sum[0]=0;
    for(int i=1;i<=n;i++)
       sum[i]=sum[i-1]+a[i];
    get_Min(n);
    get_Max(n);
}
void Circular(int a[],int n)
{
    for(int i=1;i<=n-1;i++)
        a[n+i]=a[i];
    n=2*n-1;
    straight(a, n);
    n=(n+1)/2;
    min_Circular=Min[1][n];
    max_Circular=Max[1][n];
    for(int i=2;i<=n;i++)
    {
        if(Min[i][n+i-1]<min_Circular)
           min_Circular=Min[i][n+i-1];
        if(Max[i][n+i-1]>max_Circular)
           max_Circular=Max[i][n+i-1];
    }
}

int main()
{
    int n;
    cout << "请输入石子的堆数 n:";
    cin >> n;
    cout << "请依次输入各堆的石子数:";
    for(int i=1;i<=n;i++)
       cin>>a[i];
    straight(a, n);
    cout<<"路边玩法(直线型)最小花费为:"<<Min[1][n]<<endl;
    cout<<"路边玩法(直线型)最大花费为:"<<Max[1][n]<<endl;
    Circular(a,n);
    cout<<"操场玩法(圆型)最小花费为:"<<min_Circular<<endl;
    cout<<"操场玩法(圆型)最大花费为:"<<max_Circular<<endl;
    return 0;
}

7、大卖场购物车1——0-1背包问题

//program 4-7
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 10005
#define M 105
int c[M][maxn];//c[i][j] 表示前i个物品放入容量为j购物车获得的最大价值
int w[M],v[M];//w[i] 表示第i个物品的重量,v[i] 表示第i个物品的价值
int x[M];  //x[i]表示第i个物品是否放入购物车
int main(){
    int i,j,n,W;//n表示n个物品,W表示购物车的容量
    cout << "请输入物品的个数 n:";
    cin >> n;
    cout << "请输入购物车的容量W:";
    cin >> W;
    cout << "请依次输入每个物品的重量w和价值v,用空格分开:";
    for(i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    for(i=1;i<=n;i++)//初始化第0列为0
        c[i][0]=0;
    for(j=1;j<=W;j++)//初始化第0行为0
        c[0][j]=0;
    for(i=1;i<= n;i++)//计算c[i][j]
        for(j=1;j<=W;j++)
            if(j<w[i])  //当物品的重量大于购物车的容量,则不放此物品
                c[i][j] = c[i-1][j];
            else    //否则比较此物品放与不放是否能使得购物车内的价值最大
                c[i][j] = max(c[i-1][j],c[i-1][j-w[i]] + v[i]);
    cout<<"装入购物车的最大价值为:"<<c[n][W]<<endl;

    //用于测试
    for (i=1; i<=n; i++ )
    {
        for (j=1; j<=W; j++ )
          cout << c[i][j]<<"\t" ;
        cout << endl;
    }
    cout << endl;

    //逆向构造最优解
    j=W;
    for(i=n;i>0;i--)
        if(c[i][j]>c[i-1][j])
        {
            x[i]=1;
            j-=w[i];
        }
        else
            x[i]=0;
    cout<<"装入购物车的物品序号为:";
    for(i=1;i<=n;i++)
        if(x[i]==1)
           cout<<i<<"  ";
    return 0;
}


//program 4-7
#include <iostream>
#include<cstring>
using namespace std;
#define maxn 10005
#define M 105
int dp[maxn];//dp[j] 表示当前已放入容量为j的购物车获得的最大价值
int w[M],v[M];//w[i] 表示第i个物品的重量,v[i] 表示第i个物品的价值
int x[M];  //x[i]表示第i个物品是否放入购物车
int i,j,n,W;//n表示n个物品,W表示购物车的容量
void opt1(int n,int W)
{
     for(i=1;i<=n;i++)
        for(j=W;j>0;j--)
            if(j>=w[i])  //当物品的重量大于购物车的容量,比较此物品放与不放是否能使得购物车内的价值最大
              dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}

void opt2(int n,int W)
{
     for(i=1;i<= n;i++)
        for(j=W;j>=w[i];j--)
            //当物品的重量大于购物车的容量,比较此物品放与不放是否能使得购物车内的价值最大
           dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}

void opt3(int n,int W)
{
     int sum[n];//sum[i]表示从1...i的物品重量之和
     sum[0]=0;
     for(i=1;i<=n;i++)
        sum[i]=sum[i-1]+w[i];
     for(i=1;i<=n;i++)
     {
         int bound=max(w[i],W-(sum[n]-sum[i-1]));//w[i]与剩余容量取最大值,sum[n]-sum[i-1]表示从i...n的物品重量之和
         for(j=W;j>=bound;j--)
            //当物品的重量大于购物车的容量,比较此物品放与不放是否能使得购物车内的价值最大
           dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
     }
}

int main()
{
    cout << "请输入物品的个数 n:";
    cin >> n;
    cout << "请输入购物车的容量W:";
    cin >> W;
    cout << "请依次输入每个物品的重量w和价值v,用空格分开:";
    for(i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    for(j=1;j<=W;j++)//初始化第0行为0
        dp[j]=0;
    opt1(n,W);
    //opt2(n,W);
    //opt3(n,W);
    cout<<"装入购物车的最大价值为:"<<dp[W]<<endl;

    //测试dp[]数组结果
    for(j=1;j<=W;j++)
        cout<<dp[j]<<"  ";
    cout<<endl;

   /* //逆向构造最优解,无法构造最优解
    j=W;
    for(i=n;i>0;i--)
    {
        while(dp[j]==dp[j-1])
            j--;
        if(dp[j]<=(dp[j-w[i]]+v[i]))
        {
            x[i]=1;
            j-=w[i];
        }
        else
            x[i]=0;
    }
    cout<<"装入购物车的物品为:";
    for(i=1;i<=n;i++)
        if(x[i]==1)
           cout<<i<<"  ";
    */
    return 0;
}


8、快速定位——最优二叉搜索树

//program 4-8
#include<iostream>
#include<cmath>   //求绝对值函数需要引入该头文件
using namespace std;
const int M=1000+5;
double c[M][M],w[M][M],p[M],q[M];
int s[M][M];
int n,i,j,k;
void Optimal_BST()
{
	for(i=1;i<=n+1;i++)
	{
		c[i][i-1]=0.0;
		w[i][i-1]=q[i-1];
	}
	for(int t=1;t<=n;t++)//t为关键字的规模
		//从下标为i开始的关键字到下标为j的关键字
		for(i=1;i<=n-t+1;i++)
		{
			j=i+t-1;
			w[i][j]=w[i][j-1]+p[j]+q[j];
			c[i][j]=c[i][i-1]+c[i+1][j];//初始化
			s[i][j]=i;//初始化
			//选取i+1到j之间的某个下标的关键字作为从i到j的根,如果组成的树的期望值当前最小,则k为从i到j的根节点
			for(k=i+1;k<=j;k++)
			{
				double temp=c[i][k-1]+c[k+1][j];
                if(temp<c[i][j]&&fabs(temp-c[i][j])>1E-6)//C++中浮点数因为精度问题不可以直接比较
				{
					c[i][j]=temp;
					s[i][j]=k;//k即为从下标i到j的根节点
				}
			}
			c[i][j]+=w[i][j];
		}
}
void Construct_Optimal_BST(int i,int j,bool flag)
{
	if(flag==0)
	{
		cout<<"S"<<s[i][j]<<" 是根"<<endl;
		flag=1;
	}
	int k=s[i][j];
	//如果左子树是叶子
	if(k-1<i)
	{
		cout<<"e"<<k-1<<" is the left child of "<<"S"<<k<<endl;
	}
	//如果左子树不是叶子
	else
	{
		cout<<"S"<<s[i][k-1]<<" is the left child of "<<"S"<<k<<endl;
		Construct_Optimal_BST(i,k-1,1);
	}
	//如果右子树是叶子
	if(k>=j)
	{
		cout<<"e"<<j<<" is the right child of "<<"S"<<k<<endl;
	}
	//如果右子树不是叶子
	else
	{
		cout<<"S"<<s[k+1][j]<<" is the right child of "<<"S"<<k<<endl;
		Construct_Optimal_BST(k+1,j,1);
	}
}
int main()
{
	cout << "请输入关键字的个数 n:";
    cin >> n;
    cout<<"请依次输入每个关键字的搜索概率:";
    for (i=1; i<=n; i++ )
        cin>>p[i];
    cout << "请依次输入每个虚结点的搜索概率:";
    for (i=0; i<=n; i++)
        cin>>q[i];
	Optimal_BST();
	// /*用于测试
	for(i=1; i<=n+1;i++)
    {
          for (j=1; j<i;j++)
            cout <<"\t" ;
          for(j=i-1;j<=n;j++)
            cout << w[i][j]<<"\t" ;
          cout << endl;
    }
     for(i=1; i<=n+1;i++)
    {
          for (j=1; j<i;j++)
            cout <<"\t" ;
          for(j=i-1; j<=n;j++)
            cout << c[i][j]<<"\t" ;
          cout << endl;
    }
    for(i=1; i<=n;i++)
    {
          for (j=1; j<i;j++)
            cout << "\t" ;
          for(j=i-1; j<=n;j++)
            cout << s[i][j]<<"\t" ;
          cout << endl;
    }
    cout << endl;
    // */用于测试
	cout<<"最小的搜索成本为:"<<c[1][n]<<endl;
    cout<<"最优二叉搜索树为:";
	Construct_Optimal_BST(1,n,0);
	return 0;
}

//program 4-8-1
#include<iostream>
#include<cmath>   //求绝对值函数需要引入该头文件
using namespace std;
const int M=1000+5;
double c[M][M],w[M][M],p[M],q[M];
int s[M][M];
int n,i,j,k;
void Optimal_BST()
{
	for(i=1;i<=n+1;i++)
	{
		c[i][i-1]=0.0;
		w[i][i-1]=q[i-1];
	}
	for(int t=1;t<=n;t++)//t为关键字的规模
		//从下标为i开始的关键字到下标为j的关键字
		for(i=1;i<=n-t+1;i++)
		{
			j=i+t-1;
			w[i][j]=w[i][j-1]+p[j]+q[j];
			int i1=s[i][j-1]>i?s[i][j-1]:i;
            int j1=s[i+1][j]<j?s[i+1][j]:j;
			c[i][j]=c[i][i1-1]+c[i1+1][j];//初始化
			s[i][j]=i1;//初始化
			//选取i1+1到j1之间的某个下标的关键字作为从i到j的根,如果组成的树的期望值当前最小,则k为从i到j的根节点
			for(k=i1+1;k<=j1;k++)
			{
				double temp=c[i][k-1]+c[k+1][j];
                if(temp<c[i][j]&&fabs(temp-c[i][j])>1E-6)//C++中浮点数因为精度问题不可以直接比较
				{
					c[i][j]=temp;
					s[i][j]=k;//k即为从下标i到j的根节点
				}
			}
			c[i][j]+=w[i][j];
		}
}
void Construct_Optimal_BST(int i,int j,bool flag)
{
	if(flag==0)
	{
		cout<<"S"<<s[i][j]<<" 是根"<<endl;
		flag=1;
	}
	int k=s[i][j];
	//如果左子树是叶子
	if(k-1<i)
	{
		cout<<"e"<<k-1<<" is the left child of "<<"S"<<k<<endl;
	}
	//如果左子树不是叶子
	else
	{
		cout<<"S"<<s[i][k-1]<<" is the left child of "<<"S"<<k<<endl;
		Construct_Optimal_BST(i,k-1,1);
	}
	//如果右子树是叶子
	if(k>=j)
	{
		cout<<"e"<<j<<" is the right child of "<<"S"<<k<<endl;
	}
	//如果右子树不是叶子
	else
	{
		cout<<"S"<<s[k+1][j]<<" is the right child of "<<"S"<<k<<endl;
		Construct_Optimal_BST(k+1,j,1);
	}
}
int main()
{
	cout << "请输入关键字的个数 n:";
    cin >> n;
    cout<<"请依次输入每个关键字的搜索概率:";
    for (i=1; i<=n; i++ )
        cin>>p[i];
    cout << "请依次输入每个虚结点的搜索概率:";
    for (i=0; i<=n; i++)
        cin>>q[i];
	Optimal_BST();
	// /*用于测试
	for(i=1; i<=n+1;i++)
    {
          for (j=1; j<i;j++)
            cout <<"\t" ;
          for(j=i-1;j<=n;j++)
            cout << w[i][j]<<"\t" ;
          cout << endl;
    }
     for(i=1; i<=n+1;i++)
    {
          for (j=1; j<i;j++)
            cout <<"\t" ;
          for(j=i-1; j<=n;j++)
            cout << c[i][j]<<"\t" ;
          cout << endl;
    }
    for(i=1; i<=n;i++)
    {
          for (j=1; j<i;j++)
            cout << "\t" ;
          for(j=i-1; j<=n;j++)
            cout << s[i][j]<<"\t" ;
          cout << endl;
    }
    cout << endl;
    // */用于测试
	cout<<"最小的搜索成本为:"<<c[1][n]<<endl;
    cout<<"最优二叉搜索树为:";
	Construct_Optimal_BST(1,n,0);
	return 0;
}

《趣学算法》第四章 动态规划源代码

标签:相关   遍历   字符   不用   std   ade   max   规模   绝对值   

原文地址:https://www.cnblogs.com/self-confidence/p/13604463.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!