背包问题

装箱问题

有一个箱子容量为v(正整数,0v20000),同时有n个物品(030),每个物品有一个体积(正整数)。

要求从n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入:

箱子的容量v

  物品数n

  接下来n行,分别表示这n个物品的体积

输出:

  箱子剩余空间

输入输出样例

输入:24  

6  

8    

3

12

7

9

7

输出:  0  

题解`

1. 使用回溯法计算箱子的最小剩余空间

容量为v的箱子究竟应该装入哪些物品可使得剩余空间最小。显然在n个物品的体积和小于等于v的情况下,剩余空间为v-n个物品的体积和。但在n个物品的体积和大于v的情况下,没有一种可以直接找到问题解的数学方法。无奈之下,只能采用搜索的办法。设

as为箱子的体积序列。其中a[i]为箱子i的体积,s[i]为前i个箱子的体积和1in);

best为目前所有的装箱方案中最小的剩余空间。初始时best=v

确定搜索的几个关键因素:

状态(kv):其中k为待装入箱子的物品序号,v’为箱子目前的剩余空间。

目标v若箱子的剩余空间为目前为止最小,则best调整为v’bestv);

边界条件(v-s[n]-s[k-1]))best即便剩下的物品全部装入箱子(未装物品的体积和为s[n]-s[k-1]),其剩余空间仍不小于best,则放弃当前方案,回溯;

搜索范围:在未装入全部物品的前提下(kn),搜索两种可能情况:

l         若剩余空间装得下物品kv’a[k]),则物品k装入箱子,递归计算子状态(k+1v’-a[k]);

l         物品k不装入箱子,递归计算子状态(k+1v’);

我们用递归过程search(kv)描述这一搜索过程:

procedure search(kv:integer)   {从状态(kv)出发,递归计算最小剩余空间}

    begin

  if vv            {若剩余空间为目前最小,则调整best}

    if v-(s[n]-s[k-1])>=best  {箱子即便装下全部物品,其剩余空间仍不小于best,则回溯}

then exit

    if k<=n               {在未装入全部物品的前提下搜索两种可能情况}

    then begin

           if v>=a[k]       {若剩余空间装得下物品k,则物品k装入箱子,递归计算子状态}

then search(k+1v-a[k])

            search(k+1v)       {物品k不装入箱子,递归计算子状态}

           end{then}

    end{search}

 

主程序如下:

读箱子体积v

    读物品个数n

    s[0] 0                        {物品装入前初始化}

for i1 to n do                    {输入和计算箱子的体积序列}

    begin

      读第i个箱子的体积a[i]

      s[i]s[i-1]+a[i]

    end{for}

bestv                      {初始时,最小剩余空间为箱子体积}

if s[n]<=v then bestv-s[n] {若所有物品能全部装入箱子,则剩余空间为问题解}

               else search(1v)     {否则从物品1出发,递归计算最小剩余空间}

    输出最小剩余空间best

 

2. 使用动态程序设计方法计算箱子的最小剩余空间

  如果按照物品序号依次考虑装箱顺序的话,则问题具有明显的阶段特征。问题是当前阶段的剩余空间最小,并不意味下一阶段的剩余空间也一定最小,即该问题并不具备最优子结构的特征。但如果将装箱的体积作为状态的话,则阶段间的状态转移关系顺其自然,可使得最优化问题变为判定性问题。设状态转移方程

  f[ij]——在前i个物品中选择若干个物品(必须包括物品i)装箱,其体积正好为j的标志。显然f[ij]=f[i-1j-box[i]],即物品i装入箱子后的体积正好为j的前提是f[i-1j-box[i]]=true。初始时,f[00]=true1inbox[i]jv)。

f[ij]=f[i-1j-box[i]]可以看出,当前阶段的状态转移方程仅与上一阶段的状态转移方程攸关。因此设f0i-1阶段的状态转移方程,f1i阶段的状态转移方程,这样可以将二维数组简化成一维数组。我们按照下述方法计算状态转移方程f1

fillchar(f0, sizeof(f0),0)            {装箱前,状态转移方程初始化}

    f0[0]true

    for i1 to n do             {阶段i:按照物品数递增的顺序考虑装箱情况}

      begin

        f1f0                        {i阶段的状态转移方程初始化}

        for jbox[i] to v do         {状态j:枚举所有可能的装箱体积}

          if f0[j-box[i]] then f1[j]true{若物品i装入箱子后的体积正好为j,则物品i装入箱子}

        f0f1                     {记下当前装箱情况}

  end{for}

 

经过上述运算,最优化问题转化为判定性问题。再借用动态程序设计的思想,计算装箱的最大体积     k=。显然最小剩余空间为v-k:

for iv downto 0 do        {按照递减顺序枚举所有可能的体积}

      if f1[i] then begin {若箱子能装入体积为i的物品,则输出剩余空间v-i,并退出程序}

                      writeln(v-i) halt

                    end{then}

    end.{for}

    writeln(v)         {在未装入一个物品的情况下输出箱子体积}

 

 

装箱问题

有一个箱子容量为v(正整数,0v20000),同时有n个物品(030),每个物品有一个体积(正整数)。

要求从n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入:

箱子的容量v

  物品数n

  接下来n行,分别表示这n个物品的体积

输出:

  箱子剩余空间

输入输出样例

输入:24  

6  

8    

3

12

7

9

7

输出:  0  

题解`

1. 使用回溯法计算箱子的最小剩余空间

容量为v的箱子究竟应该装入哪些物品可使得剩余空间最小。显然在n个物品的体积和小于等于v的情况下,剩余空间为v-n个物品的体积和。但在n个物品的体积和大于v的情况下,没有一种可以直接找到问题解的数学方法。无奈之下,只能采用搜索的办法。设

as为箱子的体积序列。其中a[i]为箱子i的体积,s[i]为前i个箱子的体积和1in);

best为目前所有的装箱方案中最小的剩余空间。初始时best=v

确定搜索的几个关键因素:

状态(kv):其中k为待装入箱子的物品序号,v’为箱子目前的剩余空间。

目标v若箱子的剩余空间为目前为止最小,则best调整为v’bestv);

边界条件(v-s[n]-s[k-1]))best即便剩下的物品全部装入箱子(未装物品的体积和为s[n]-s[k-1]),其剩余空间仍不小于best,则放弃当前方案,回溯;

搜索范围:在未装入全部物品的前提下(kn),搜索两种可能情况:

l         若剩余空间装得下物品kv’a[k]),则物品k装入箱子,递归计算子状态(k+1v’-a[k]);

l         物品k不装入箱子,递归计算子状态(k+1v’);

我们用递归过程search(kv)描述这一搜索过程:

procedure search(kv:integer)   {从状态(kv)出发,递归计算最小剩余空间}

    begin

  if vv            {若剩余空间为目前最小,则调整best}

    if v-(s[n]-s[k-1])>=best  {箱子即便装下全部物品,其剩余空间仍不小于best,则回溯}

then exit

    if k<=n               {在未装入全部物品的前提下搜索两种可能情况}

    then begin

           if v>=a[k]       {若剩余空间装得下物品k,则物品k装入箱子,递归计算子状态}

then search(k+1v-a[k])

            search(k+1v)       {物品k不装入箱子,递归计算子状态}

           end{then}

    end{search}

 

主程序如下:

读箱子体积v

    读物品个数n

    s[0] 0                        {物品装入前初始化}

for i1 to n do                    {输入和计算箱子的体积序列}

    begin

      读第i个箱子的体积a[i]

      s[i]s[i-1]+a[i]

    end{for}

bestv                      {初始时,最小剩余空间为箱子体积}

if s[n]<=v then bestv-s[n] {若所有物品能全部装入箱子,则剩余空间为问题解}

               else search(1v)     {否则从物品1出发,递归计算最小剩余空间}

    输出最小剩余空间best

 

2. 使用动态程序设计方法计算箱子的最小剩余空间

  如果按照物品序号依次考虑装箱顺序的话,则问题具有明显的阶段特征。问题是当前阶段的剩余空间最小,并不意味下一阶段的剩余空间也一定最小,即该问题并不具备最优子结构的特征。但如果将装箱的体积作为状态的话,则阶段间的状态转移关系顺其自然,可使得最优化问题变为判定性问题。设状态转移方程

  f[ij]——在前i个物品中选择若干个物品(必须包括物品i)装箱,其体积正好为j的标志。显然f[ij]=f[i-1j-box[i]],即物品i装入箱子后的体积正好为j的前提是f[i-1j-box[i]]=true。初始时,f[00]=true1inbox[i]jv)。

f[ij]=f[i-1j-box[i]]可以看出,当前阶段的状态转移方程仅与上一阶段的状态转移方程攸关。因此设f0i-1阶段的状态转移方程,f1i阶段的状态转移方程,这样可以将二维数组简化成一维数组。我们按照下述方法计算状态转移方程f1

fillchar(f0, sizeof(f0),0)            {装箱前,状态转移方程初始化}

    f0[0]true

    for i1 to n do             {阶段i:按照物品数递增的顺序考虑装箱情况}

      begin

        f1f0                        {i阶段的状态转移方程初始化}

        for jbox[i] to v do         {状态j:枚举所有可能的装箱体积}

          if f0[j-box[i]] then f1[j]true{若物品i装入箱子后的体积正好为j,则物品i装入箱子}

        f0f1                     {记下当前装箱情况}

  end{for}

 

经过上述运算,最优化问题转化为判定性问题。再借用动态程序设计的思想,计算装箱的最大体积     k=。显然最小剩余空间为v-k:

for iv downto 0 do        {按照递减顺序枚举所有可能的体积}

      if f1[i] then begin {若箱子能装入体积为i的物品,则输出剩余空间v-i,并退出程序}

                      writeln(v-i) halt

                    end{then}

    end.{for}

    writeln(v)         {在未装入一个物品的情况下输出箱子体积}

 

 

你可能感兴趣的:(search,integer,优化)