0029算法笔记回溯法n后问题和01背包问题.docx

上传人:b****3 文档编号:3538595 上传时间:2022-11-23 格式:DOCX 页数:15 大小:73.99KB
下载 相关 举报
0029算法笔记回溯法n后问题和01背包问题.docx_第1页
第1页 / 共15页
0029算法笔记回溯法n后问题和01背包问题.docx_第2页
第2页 / 共15页
0029算法笔记回溯法n后问题和01背包问题.docx_第3页
第3页 / 共15页
0029算法笔记回溯法n后问题和01背包问题.docx_第4页
第4页 / 共15页
0029算法笔记回溯法n后问题和01背包问题.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

0029算法笔记回溯法n后问题和01背包问题.docx

《0029算法笔记回溯法n后问题和01背包问题.docx》由会员分享,可在线阅读,更多相关《0029算法笔记回溯法n后问题和01背包问题.docx(15页珍藏版)》请在冰豆网上搜索。

0029算法笔记回溯法n后问题和01背包问题.docx

0029算法笔记回溯法n后问题和01背包问题

1、n后问题

   问题描述:

在n×n格的棋盘上放置彼此不受攻击的n个皇后。

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。

   问题解析:

用n元数组x[1:

n]表示n后问题的解。

其中,x[i]表示皇后i放在棋盘的第i行的第x[i]列。

由于不允许将2个皇后放在同一列上,所以解向量中的x[i]互不相同。

如果将n*n的棋盘看做是二维方阵,其行号从上到下,列号从左到右依次编号为1,2,……n。

设两个皇后的坐标分别为(i,j)和(k,l)。

若两个皇后在同一斜线上,那么这两个皇后的坐标连成的线为1或者-1。

因此有:

   由此约束条件剪去不满足行、列和斜线约束的子树。

程序的递归回溯实现如下:

[cpp] viewplain copy

1.//n后问题 回溯法计算 递归  

2.#include "stdafx.h"  

3.#include   

4.#include "math.h"  

5.using namespace std;   

6.  

7.class Queen  

8.{  

9.   friend int nQueen(int);  

10.   private:

  

11.      bool Place(int k);  

12.      void Backtrack(int t);  

13.      int  n,    // 皇后个数  

14.          *x;    // 当前解  

15.      long sum;  // 当前已找到的可行方案数    

16.};   

17.  

18.int main()  

19.{  

20.    int n=4,m;  

21.    cout<

"<

22.    m=nQueen(n);  

23.    cout<

24.    cout<

"<

25.    return 0;  

26.}  

27.  

28.bool Queen:

:

Place(int k)  

29.{  

30.    for (int j=1;j

31.    {  

32.        if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))   

33.        {  

34.            return false;  

35.        }  

36.    }  

37.    return true;  

38.}   

39.  

40.void Queen:

:

Backtrack(int t)//t扩展的是行  

41.{  

42.    if (t>n)  

43.    {  

44.        sum++;  

45.        for (int i=1;i<=n;i++)  

46.        {  

47.            cout<

48.        }  

49.        cout<

50.    }  

51.    else  

52.    {  

53.        //探索第t行的每一列是否有元素满足要求  

54.        for (int i=1;i<=n;i++)  

55.        {  

56.            x[t]=i;  

57.            if (Place(t))  

58.            {  

59.                Backtrack(t+1);  

60.            }  

61.        }  

62.    }  

63. }  

64.  

65.int nQueen(int n)  

66.{  

67.    Queen X;  

68.    X.n=n;  

69.    X.sum=0;  

70.  

71.    int *p=new int[n+1];  

72.  

73.    for(int i=0;i<=n;i++)  

74.    {  

75.        p[i]=0;  

76.    }  

77.  

78.    X.x=p;  

79.    X.Backtrack

(1);  

80.  

81.    delete []p;  

82.    return X.sum;  

83.}  

   数组x记录了解空间树中从根到当前扩展节点的路径,这些信息包含了回溯法在回溯是所需要的信息。

利用数组x所含的信息,可将上述回溯法表示成非递归的形式。

进一步省去O(n)递归栈空间。

迭代实现的n后问题具体代码如下:

[cpp] viewplain copy

1.//n后问题 回溯法计算 迭代  

2.#include "stdafx.h"  

3.#include   

4.#include "math.h"  

5.using namespace std;   

6.  

7.class Queen  

8.{  

9.   friend int nQueen(int);  

10.   private:

  

11.      bool Place(int k);  

12.      void Backtrack(void);  

13.      int  n,    // 皇后个数  

14.          *x;    // 当前解  

15.      long sum;  // 当前已找到的可行方案数    

16.};   

17.  

18.int main()  

19.{  

20.    int n=4,m;  

21.    cout<

"<

22.    m=nQueen(n);  

23.    cout<

24.    cout<

"<

25.    return 0;  

26.}  

27.  

28.bool Queen:

:

Place(int k)  

29.{  

30.    for (int j=1;j

31.    {  

32.        if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))   

33.        {  

34.            return false;  

35.        }  

36.    }  

37.    return true;  

38.}   

39.  

40.void Queen:

:

Backtrack()  

41.{  

42.    x[1] = 0;  

43.    int k = 1;  

44.    while(k>0)  

45.    {  

46.        x[k] += 1;  

47.        while((x[k]<=n)&&!

(Place(k)))//寻找能够放置皇后的位置  

48.        {  

49.            x[k] += 1;  

50.        }  

51.  

52.        if(x[k]<=n)//找到位置  

53.        {  

54.            if(k == n)  

55.            {  

56.                for (int i=1;i<=n;i++)  

57.                {  

58.                    cout<

59.                }  

60.                cout<

61.                sum++;  

62.            }  

63.            else  

64.            {  

65.                k++;  

66.                x[k]=0;  

67.            }  

68.        }  

69.        else  

70.        {  

71.            k--;  

72.        }  

73.    }  

74. }  

75.  

76.int nQueen(int n)  

77.{  

78.    Queen X;  

79.    X.n=n;  

80.    X.sum=0;  

81.  

82.    int *p=new int[n+1];  

83.  

84.    for(int i=0;i<=n;i++)  

85.    {  

86.        p[i]=0;  

87.    }  

88.  

89.    X.x=p;  

90.    X.Backtrack();  

91.  

92.    delete []p;  

93.    return X.sum;  

94.}  

  程序运行结果如图:

   

   2、0-1背包问题

   问题描述:

  

   给定n种物品和一背包。

物品i的重量是wi,其价值为vi,背包的容量为C。

问:

应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

   形式化描述:

给定c>0,wi>0,vi>0,1≤i≤n.要求找一n元向量(x1,x2,…,xn,),xi∈{0,1},∋∑wixi≤c,且∑vixi达最大.即一个特殊的整数规划问题。

   问题解析:

0-1背包问题是子集选取问题。

0-1背包问题的解空间可以用子集树表示。

在搜索解空间树时,只要其左儿子节点是一个可行节点,搜索就进入左子树。

当右子树中有可能含有最优解时,才进入右子树搜索。

否则,将右子树剪去。

设r是当前剩余物品价值总和,cp是当前价值;bestp是当前最优价值。

当cp+r<=bestp时,可剪去右子树。

计算右子树上界的更好的方法是将剩余物品依次按其单位价值排序,然后依次装入物品,直至装不下时,再装入物品一部分而装满背包。

   例如:

对于0-1背包问题的一个实例,n=4,c=7,p=[9,10,7,4],w=[3,5,2,1]。

这4个物品的单位重量价值分别为[3,2,3,5,4]。

以物品单位重量价值的递减序装入物品。

先装入物品4,然后装入物品3和1.装入这3个物品后,剩余的背包容量为1,只能装0.2的物品2。

由此得一个解为[1,0.2,1,1],其相应价值为22。

尽管这不是一个可行解,但可以证明其价值是最优值的上界。

因此,对于这个实例,最优值不超过22。

   在实现时,由Bound计算当前节点处的上界。

类Knap的数据成员记录解空间树中的节点信息,以减少参数传递调用所需要的栈空间。

在解空间树的当前扩展节点处,仅要进入右子树时才计算上界Bound,以判断是否可将右子树剪去。

进入左子树时不需要计算上界,因为上界预期父节点的上界相同。

算法的具体实现如下:

[cpp] viewplain copy

1.//0-1背包问题 回溯法求解  

2.#include "stdafx.h"  

3.#include   

4.using namespace std;   

5.  

6.template  

7.class Knap  

8.{  

9.    template  

10.    friend Typep Knapsack(Typep [],Typew [],Typew,int);  

11.    private:

  

12.        Typep Bound(int i);  

13.        void Backtrack(int i);  

14.  

15.        Typew c;    //背包容量  

16.        int n;      //物品数  

17.  

18.        Typew *w;   //物品重量数组  

19.        Typep *p;   //物品价值数组  

20.        Typew cw;   //当前重量  

21.  

22.        Typep cp;   //当前价值  

23.        Typep bestp;//当前最后价值  

24.};  

25.  

26.template  

27.Typep Knapsack(Typep p[],Typew w[],Typew c,int n);  

28.  

29.template   

30.inline void Swap(Type &a,Type &b);  

31.  

32.template  

33.void BubbleSort(Type a[],int n);  

34.  

35.int main()  

36.{  

37.    int n = 4;//物品数  

38.    int c = 7;//背包容量  

39.    int p[] = {0,9,10,7,4};//物品价值 下标从1开始  

40.    int w[] = {0,3,5,2,1};//物品重量 下标从1开始  

41.  

42.    cout<<"背包容量为:

"<

43.    cout<<"物品重量和价值分别为:

"<

44.  

45.    for(int i=1; i<=n; i++)  

46.    {  

47.        cout<<"("<

48.    }  

49.    cout<

50.  

51.    cout<<"背包能装下的最大价值为:

"<

52.    return 0;  

53.}  

54.  

55.template  

56.void Knap:

:

Backtrack(int i)  

57.{  

58.    if(i>n)//到达叶子节点  

59.    {  

60.        bestp = cp;  

61.        return;  

62.    }  

63.  

64.    if(cw + w[i] <= c)//进入左子树  

65.    {  

66.        cw += w[i];  

67.        cp += p[i];  

68.        Backtrack(i+1);  

69.        cw -= w[i];  

70.        cp -= p[i];  

71.    }  

72.  

73.    if(Bound(i+1)>bestp)//进入右子树  

74.    {  

75.        Backtrack(i+1);  

76.    }  

77.}  

78.  

79.template  

80.Typep Knap:

:

Bound(int i)// 计算上界  

81.{  

82.    Typew cleft = c - cw;  // 剩余容量  

83.    Typep b = cp;  

84.  

85.    // 以物品单位重量价值递减序装入物品  

86.    while (i <= n && w[i] <= cleft)   

87.    {  

88.        cleft -= w[i];  

89.        b += p[i];  

90.        i++;  

91.    }  

92.  

93.   // 装满背包  

94.   if (i <= n)  

95.   {  

96.       b += p[i]/w[i] * cleft;  

97.   }  

98.  

99.   return b;  

100.}  

101.  

102.class Object  

103.{  

104.    template  

105.    friend Typep Knapsack(Typep[],Typew [],Typew,int);  

106.    public:

  

107.        int operator <= (Object a)const  

108.        {  

109.            return (d>=a.d);  

110.        }  

111.    private:

  

112.        int ID;  

113.        float d;      

114.};  

115.  

116.template  

117.Typep Knapsack(Typep p[],Typew w[],Typew c,int n)  

118.{  

119.    //为Knap:

:

Backtrack初始化  

120.    Typew W = 0;  

121.    Typep P = 0;  

122.  

123.    Object *Q = new Object[n];  

124.    for(int i=1; i<=n; i++)  

125.    {  

126.        Q[i-1].ID = i;  

127.        Q[i-1].d = 1.0 * p[i]/w[i];  

128.        P += p[i];  

129.        W += w[i];  

130.    }  

131.  

132.    if(W <= c)//装入所有物品  

133.    {  

134.        return P;  

135.    }  

136.  

137.    //依物品单位重量价值排序  

138.    BubbleSort(Q,n);  

139.  

140.    Knap K;  

141.    K.p = new Typep[n+1];  

142.    K.w = new Typew[n+1];  

143.  

144.    for(int i=1; i<=n; i++)  

145.    {  

146.        K.p[i] = p[Q[i-1].ID];  

147.        K.w[i] = w[Q[i-1].ID];  

148.    }  

149.  

150.    K.cp = 0;  

151.    K.cw = 0;  

152.    K.c = c;  

153.    K.n = n;  

154.    K.bestp = 0;  

155.  

156.    //回溯搜索  

157.    K.Backtrack

(1);  

158.  

159.    delete []Q;  

160.    delete []K.w;  

161.    delete []K.p;  

162.    return K.bestp;  

163.}  

164.  

165.template  

166.void BubbleSort(Type a[],int n)  

167.{  

168.     //记录一次遍历中是否有元素的交换     

169.     bool exchange;    

170.     for(int i=0; i

171.     {    

172.        exchange = false ;    

173.        for(int j=i+1; j<=n-1; j++)    

174.        {    

175.            if(a[j]<=a[j-1])    

176.            {    

177.                Swap(a[j],a[j-1]);   

178.                exchange = true;    

179.            }     

180.        }     

181.        //如果这次遍历没有元素的交换,那么排序结束     

182.        if(false == exchange)    

183.        {  

184.             break ;    

185.        }  

186.     }  

187.}  

188.  

189.template 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1