动态规划

动态规划方法生成最优二叉查找树

 转自:http://www.cnblogs.com/lpshou/archive/2012/04/26/2470914.html

1、概念引入

  基于统计先验知识,我们可统计出一个数表(集合)中各元素的查找概率,理解为集合各元素的出现频率。比如中文输入法字库中各词条(单字、词组等)的先验概率,针对用户习惯可以自动调整词频——所谓动态调频、高频先现原则,以减少用户翻查次数。这就是最优二叉查找树问题:查找过程中键值比较次数最少,或者说希望用最少的键值比较次数找到每个关键码(键值)。为解决这样的问题,显然需要对集合的每个元素赋予一个特殊属性——查找概率。这样我们就需要构造一颗最优二叉查找树。
 
2、问题给出
  n个键{a1,a2,a3......an},其相应的查找概率为{p1,p2,p3......pn}。构成最优BST,表示为T 1 n ,求这棵树的平均查找次数C[1, n](耗费最低)。换言之,如何构造这棵最优BST,使得
C[1, n] 最小。
 
3、分段方法
   
    动态规划法策略是将问题分成多个阶段,逐段推进计算,后继实例解由其直接前趋实例解计算得到。对于最优BST问题,利用减一技术和最优性原则,如果前n-1个节点构成最优BST,加入一个节点 an 后要求构成规模n的最优BST。按 n-1, n-2 , ... , 2, 1 递归,问题可解。自底向上计算:C[1, 2]→C[1, 3] →... →C[1, n]。为不失一般性用
C[i, j] 表示由{a1,a2,a3......an}构成的BST的耗费。其中1≤i ≤j ≤n。这棵树表示为Tij。从中选择一个键ak作根节点,它的左子树为Tik-1,右子树为Tk+1j。要求选择的k 使得整棵树的平均查找次数C[i, j]最小。左右子树递归执行此过程。(根的生成过程)
 
 4、递推计算式
 

 

  5、基本算法如下
  

6、具体实现代码(其中所有数据都存放在2.txt中,其内容为:

其中5表示有5个节点,其他数据表示各个节点出现的概率;

复制代码
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #define max 9999
 4 void OptimalBST(int,float*,float**,int**);
 5 void OptimalBSTPrint(int,int,int**);
 6 void main()
 7 {
 8     int i,num;
 9     FILE *point;
10     //所有数据均从2.txt中获取,2.txt中第一个数据表示节点个数;从第二个数据开始表示各个节点的概率
11     point=fopen("2.txt","r");
12     if(point==NULL)
13     {
14         printf("cannot open 2.txt.\n");
15         exit(-1);
16     }
17     fscanf(point,"%d",&num);
18     printf("%d\n",num);
19     float *p=(float*)malloc(sizeof(float)*(num+1));
20     for(i=1;i<num+1;i++)
21         fscanf(point,"%f",&p[i]);
22     //创建主表;
23     float **c=(float**)malloc(sizeof(float*)*(num+2));
24     for(i=0;i<num+2;i++)
25         c[i]=(float*)malloc(sizeof(float)*(num+1));
26     //创建根表;
27     int **r=(int**)malloc(sizeof(int*)*(num+2));
28     for(i=0;i<num+2;i++)
29         r[i]=(int*)malloc(sizeof(int)*(num+1));
30     //动态规划实现最优二叉查找树的期望代价求解。。
31     OptimalBST(num,p,c,r);
32     printf("该最优二叉查找树的期望代价为:%f \n",c[1][num]);
33     //给出最优二叉查找树的中序遍历结果;
34     printf("构造成的最优二叉查找树的中序遍历结果为:");
35     OptimalBSTPrint(1,4,r);
36 
37 }
38 void OptimalBST(int num,float*p,float**c,int**r)
39 {
40     int d,i,j,k,s,kmin;
41     float temp,sum;
42     for(i=1;i<num+1;i++)//主表和根表元素的初始化
43     {
44     
45         c[i][i-1]=0;
46         c[i][i]=p[i];
47         r[i][i]=i;
48     }
49     c[num+1][num]=0;
50     for(d=1;d<=num-1;d++)//加入节点序列
51     {
52         for(i=1;i<=num-d;i++)
53         {
54             j=i+d;
55             temp=max;
56             for(k=i;k<=j;k++)//找最优根
57             {
58                 if(c[i][k-1]+c[k+1][j]<temp)
59                 {
60                     temp=c[i][k-1]+c[k+1][j];
61                     kmin=k;
62                 }
63             }
64             r[i][j]=kmin;//记录最优根
65             sum=p[i];
66             for(s=i+1;s<=j;s++)
67                 sum+=p[s];
68             c[i][j]=temp+sum;
69         }
70     }
71 }
72 //采用递归方式实现最优根的输出,最优根都是保存在r[i][j]中的。。。
73 void OptimalBSTPrint(int first,int last,int**r)
74 {
75 
76     int k;
77     if(first<=last)
78     {
79         k=r[first][last];
80         printf("%d  ",k);
81         OptimalBSTPrint(first,k-1,r);
82         OptimalBSTPrint(k+1,last,r);
83     }
84 }
复制代码

7、最终运行结果:

8、参考文献:

(1)算法导论

(2)数据结构 严蔚敏

(3)网上下载的一个ppt(算法设计与分析,第八章)

另附:http://blog.sina.com.cn/s/blog_51cea4040100gk6v.html

动态规划求0-1背包问题

1.问题描述如下:

经过分析后算法如下:v(i,j)表示前j个物品中能够放入承重量为j的包中的最大价值。。。。

 

2.具体的代码实现如下:

  其中2.txt中的具体数据为:

   代码:

View Code
复制代码
 1  #include<stdio.h>
 2   #include<stdlib.h>
 3   void maxValue(int *,int *,int **,int,int);
 4   int ziji(int *,int *,int *,int **,int,int);
 5   int max(int,int);
 6   void main()
 7   {
 8       int i,k,num,weight;
 9       FILE *p;
10      p=fopen("2.txt","r");
11      if(p==NULL)
12      {
13          printf("文件打开失败!");
14          exit(1);
15      }
16      fscanf(p,"%d%d",&num,&weight);
17      int *w=(int*)malloc(sizeof(int)*num);
18      int *v=(int*)malloc(sizeof(int)*num);
19      int *path=(int*)malloc(sizeof(int)*num);
20      int **vv=(int **)malloc(sizeof(int *)*(num+1));
21      for(i=0;i<num+1;i++)
22          vv[i]=(int *)malloc(sizeof(int)*(weight+1));
23      for(i=1;i<=num;i++)
24          fscanf(p,"%d",&w[i]);
25      for(i=1;i<=num;i++)
26          fscanf(p,"%d",&v[i]);
27  
28          /*printf("%d %d\n",num,weight);
29          for(i=0;i<num;i++)
30              printf("%d\n",w[i]);
31          for(i=0;i<num;i++)
32              printf("%d\n",v[i]);*/
33      maxValue(w,v,vv,num,weight);
34      printf("最终最大价值为: %d\n",vv[num][weight]);
35      k=ziji(w,v,path,vv,num,weight);
36      printf("最优子集为: \n");
37      for(i=0;i<k;i++)
38          printf("%d  ",path[i]);
39  }
40  //求最终最大的价值
41  void maxValue(int *w,int *v,int **vv,int num,int weight)
42  {     
43      int i,j,k;
44      for(i=0;i<=num;i++)
45          vv[i][0]=0;
46      for(i=0;i<=weight;i++)
47          vv[0][i]=0;
48      for(i=1;i<=num;i++)
49          for(j=1;j<=weight;j++)
50          {
51              if(j>=w[i])
52              {
53                  k=j-w[i];
54                  vv[i][j]=max((v[i]+vv[i-1][k]),vv[i-1][j]);
55              }
56              else
57                  vv[i][j]=vv[i-1][j];//谢谢楼下的提醒,此处已更正。。
58          }
59  }
60  //求最优子集
61  int ziji(int *w,int *v,int *path,int **vv,int num,int weight)
62  {
63      int i,j=5,k=0;
64      for(i=num;i>0;i--)
65      {
66          if(j>0)
67          {
68              if(vv[i][j]!=vv[i-1][j])
69              {
70                  path[k++]=i;
71                  j=j-w[i];
72              }
73          }
74      }
75      return k;
76  }
77  int max(int x,int y)
78  {
79      if(x>y) return x;
80      else return y;
81  }
复制代码

 附:

  另外附两幅网上找到的ppt,以便于理解,参考:http://wenku.baidu.com/view/b9e4f70416fc700abb68fce0.html

    (1)最大价值v(4,5)

  (2)回溯法找到最优子集

动态规划求数塔问题

问题描述如下:


 

具体解决方案如下:

{    其中2.txt中保存的数据为:

   

}

复制代码
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 int m;
 4 void dqta(int **data,int **d);
 5 int max(int,int);
 6 void fpath(int **data,int **d,int *path);
 7 void main()
 8 {
 9     int i,j;
10     FILE *p;
11     p=fopen("2.txt","r");
12     if(p==NULL)
13     {
14         printf("读取文件失败!\n");
15         exit(1);
16     }
17     else
18         fscanf(p,"%d",&m);
19     //创建动态数组;
20     int **data=(int **)malloc(sizeof(int *)*m);
21     for(i=0;i<m;i++)
22         data[i]=(int *)malloc(sizeof(int)*m);
23     int **d=(int **)malloc(sizeof(int *)*m);
24     for(i=0;i<m;i++)
25         d[i]=(int *)malloc(sizeof(int)*m);
26 
27     int *path=(int *)malloc(sizeof(int)*m);
28     //读取文件中的数据;
29     for(i=0;i<m;i++)
30         for(j=0;j<=i;j++)
31             fscanf(p,"%d",&data[i][j]);
32     dqta(data,d);
33     printf("最大值为:%d\n",d[0][0]);
34     fpath(data,d,path);
35     printf("最终路径为:\n");
36         for(i=0;i<m;i++)
37             printf("%d\n",path[i]);
38 }
39 int max(int x,int y)
40 {
41     if(x>y)return x;
42     else return y;
43 }
44 void dqta(int **data,int **d)
45 {
46     int i,j;
47         for(j=0;j<m;j++)
48             d[m-1][j]=data[m-1][j];
49     for(i=m-2;i>=0;i--)
50         for(j=0;j<=i;j++)
51             d[i][j]=max(d[i+1][j],d[i+1][j+1])+data[i][j];
52 }
53 void fpath(int **data,int **d,int *path)
54 {
55     int i,j,b;
56     path[0]=data[0][0];
57     j=0;
58     for(i=1;i<m;i++)
59     {
60         b=d[i-1][j]-data[i-1][j];
61         if(b==d[i][j])
62         {
63             path[i]=data[i][j];
64         }
65         else
66         {
67             path[i]=data[i][j+1];
68             j++;
69         }
70     }
71 }
复制代码

动态规划求有向无环图的单源最短路径

问题描述如下:


 

 


 

具体代码实现:

复制代码
 1 #include<stdlib.h>
 2 #include<stdio.h>
 3 #define x 9999
 4 #define max 9999
 5 int data[10][10];
 6 int dist[10];//记录最短路径为多少
 7 int path[10];//记录最短路径
 8 int kmin(int,int);
 9 void fpath(int a[][10]);
10 int froute(int a[][10]);
11 void main()
12 {
13     int i,m;
14     int a[10][10]={
15     {x,4,2,3,x,x,x,x,x,x},
16     {x,x,x,x,10,9,x,x,x,x},
17     {x,x,x,x,6,7,10,x,x,x},
18     {x,x,x,x,x,3,8,x,x,x},
19     {x,x,x,x,x,x,x,4,8,x},
20     {x,x,x,x,x,x,x,9,6,x},
21     {x,x,x,x,x,x,x,5,4,x},
22     {x,x,x,x,x,x,x,x,x,8},
23     {x,x,x,x,x,x,x,x,x,4},
24     {x,x,x,x,x,x,x,x,x,x}};
25 
26     /*for (i=0;i<10;i++)
27     {
28         for(j=0;j<10;j++)
29         printf("%d  ",a[i][j]);
30         printf("\n");
31     }*/
32     fpath(a);
33         printf("最短路径大小为:  %d\n",dist[9]);
34 
35     m=froute(a);
36     for(i=m-1;i>=0;i--)
37         printf("最短路径经过:   %d\n",path[i]);
38 }
39 void fpath(int a[][10])
40 {
41     int i,j,k;
42     dist[0]=0;
43     for(i=1;i<10;i++)
44     {
45         k=max;
46         for(j=0;j<i;j++)
47         {
48             if(a[j][i]!=x)
49                 if((dist[j]+a[j][i])<k)
50                     k=dist[j]+a[j][i];    
51         }
52         dist[i]=k;
53     }
54 }
55 int froute(int a[][10])
56 {
57     int j,b,k=1,i=9;
58     path[0]=10;
59     while(i>0)
60     {
61         for(j=i-1;j>=0;j--)
62         {
63             if(a[j][i]!=x)
64             {
65                 b=dist[i]-a[j][i];
66                 if(b==dist[j])
67                 {
68                     path[k++]=j+1;
69                     i=j;
70                     break;
71                 }
72             }
73 
74         }
75     }
76         return k;
77 }
78 
79 
80             
复制代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值