0-1 背包问题的扩展
问题提出:
给定 n种物品和一背包, 物品的重量是 wi, 体积为 bi, 价值为 vi, 背包的容量为 C, 容积为 D, 问应如何选择装入背包中的物品, 使得装入背包中物品的总价值最大? 在选择装入背包的物品时, 对每种物品 i 只有两种选择, 即装入背包或不装入背包。不能将物品 i 装入背包多次, 也不能只装入部分物品 i。
算法分析:
代码实现:
Code
1/**//* 0-背包问题扩展
2 * By Flouse@2008年12月9日 17时06分28秒
3 */
4using System;
5using System.Collections.Generic;
6using System.Text;
7
8namespace YourNameSpace // 修改为统一的命名空间
9{
10 /**//// <summary>装车类</summary>
11 public class Loading
12 {
13 /**//// <summary>货车载重量(t)</summary>
14 private int capacity;
15 /**//// <summary>货车体积容量(m3)</summary>
16 private int cubage;
17 /**//// <summary>最优(运费最高)装载清单</summary>
18 private List<string> optimizedBills = new List<string>();
19 public float totalPrice;
20
21 /**//// <summary>运单信息(装载相关)</summary>
22 public struct TranBill
23 {
24 /**//// <summary>托运单号</summary>
25 public string tranID;
26 /**//// <summary>拆单号</summary>
27 public int tranSplitID;
28 /**//// <summary>重量(t),实际运算里四舍五入化整</summary>
29 public float billWeight;
30 /**//// <summary>体积(m3),实际运算里四舍五入化整</summary>
31 public float billVolumn;
32 /**//// <summary>运费</summary>
33 public float tranPrice;
34 };
35
36 /**//// <summary>初始化货车信息</summary>
37 /// <param name="capacity">货车载重量(t)</param>
38 /// <param name="cubage">货车体积容量(m3)</param>
39 Loading(int capacity, int cubage)
40 {
41 this.capacity = capacity;
42 this.cubage = cubage;
43 }
44 ~Loading()
45 {
46 GC.Collect();
47 //?? 释放内存
48 }
49 /**//// <summary>求解最优装车方案</summary>
50 /// <param name="tbs">待运的托运单</param>
51 /// <param name="mode">重量、体积转整模式
52 /// <value>0.0f~1.0f, 当 mode = 0.5f 时,即为四舍五入方式;当 mode = 0.0f 时, 即为最贪婪方式;当 mode = 1.0f 时,即为最保险方式</value>
53 /// <example>(int)(billWeight + mode), (int)(billVolumn + mode)</example>
54 /// </param>
55 /// <returns></returns>
56 public List<string> doLoad(TranBill[] tbs, float mode)
57 {
58 int count = tbs.Length;
59 int[] w = new int[count + 1];
60 int[] v = new int[count + 1];
61 float[] p = new float[count + 1];
62 float[, ,] m = new float[count + 1, capacity + 1, cubage + 1];
63 for (int i = 1; i <= count; i++)
64 {
65 w[i] = (int)(tbs[i - 1].billWeight + mode);
66 v[i] = (int)(tbs[i - 1].billVolumn + mode);
67 p[i] = tbs[i - 1].tranPrice;
68 }
69 Knapsack(p, w, v, count, m);
70 Trackback(m, w, v, tbs);
71 return optimizedBills;
72 }
73
74 private void Trackback(float[, ,] m, int[] w, int[] v, TranBill[] tbs)
75 {
76 int _capacity = capacity, _cubage = cubage, n = tbs.Length;
77 optimizedBills.Clear();
78 for (int i = 1; i < n; i++)
79 {
80 if (m[i, _capacity, _cubage] - m[i + 1, _capacity, _cubage] > 0.000001f)
81 {
82 optimizedBills.Add(tbs[i - 1].tranID + '.' + tbs[i-1].tranSplitID);
83 _capacity -= w[i];
84 _cubage -= v[i];
85 }
86 }
87 if (m[n, _capacity, _cubage] > 0) optimizedBills.Add(tbs[n - 1].tranID + '.' + tbs[n - 1].tranSplitID);
88 }
89
90 /**//// <summary>采用动态规划标记法(0-1背包问题算法)</summary>
91 /// <param name="p"></param>
92 /// <param name="w"></param>
93 /// <param name="v"></param>
94 /// <param name="n"></param>
95 /// <param name="m"></param>
96 private void Knapsack(float[] p, int[] w, int[] v, int n, float[, ,] m)
97 {
98 int wMax = Math.Min(w[n] - 1, capacity);
99 int vMax = Math.Min(v[n] - 1, cubage);
100
101 int i, j, k;
102
103 for (i = 0, j = 0; i <= wMax; i++)
104 for (j = 0; j < cubage; j++)
105 m[n, i, j] = 0;
106 for (j = 0; j <= vMax; j++)
107 for (i = wMax+1; i < capacity; i++)
108 m[n, i, j] = 0;
109
110 for (i = w[n]; i <= capacity; i++)
111 for (j = v[n]; j <= cubage; j++)
112 m[n, i, j] = p[n];
113
114 for (k = n - 1; k > 1; k--)
115 {
116 wMax = Math.Min(w[k] - 1, capacity);
117 vMax = Math.Min(v[k] - 1, cubage);
118
119 for (i = 0, j = 0; i <= wMax; i++)
120 for (j = 0; j < cubage; j++)
121 m[k, i, j] = m[k + 1, i, j];
122 for (j = 0; j <= vMax; j++)
123 for (i = wMax+1; i < capacity; i++)
124 m[k, i, j] = m[k + 1, i, j];
125
126 for (i = w[k]; i <= capacity; i++)
127 for (j = v[k]; j <= cubage; j++)
128 m[k, i, j] = Math.Max(m[k + 1, i, j], m[k + 1, i - w[k], j - v[k]] + p[k]);
129 }
130 m[1, capacity, cubage] = m[2, capacity, cubage];
131 if (capacity >= w[1] && cubage >= v[1]) m[1, capacity, cubage] = Math.Max(m[1, capacity, cubage], m[2, capacity - w[1], cubage - v[1]] + p[1]);
132
133 totalPrice = m[1, capacity, cubage];
134 }
135
136 // 回溯法,示实现
137 //class BackBag{
138 //public:
139 // BackBag(int W){
140 // maxW = W;
141 // ac = maxValue = nowValue = 0;
142 // }
143 // void KnapSack(int v[], int c[], bool used[], int n){ //回溯法
144 // for (int i = 0; i < n; i++){
145 // nowValue += v[i];
146 // ac += c[i];
147 // used[i] = true;
148 // }
149 // search(0, v, c, used, n);
150 // }
151 // void search(int i, int v[], int c[], bool used[], int n){
152 // if (i==n) return;
153 // if (nowValue <= maxValue) return;
154 // if (ac <= maxW){
155 // maxValue = nowValue;
156 // return;
157 // }
158 // nowValue -= v[i];
159 // ac -= c[i];
160 // used[i] = false;
161 // search(i+1, v, c, used, n); //考虑不取i
162 // nowValue += v[i];
163 // ac += c[i];
164 // used[i] = true;
165 // search(i+1, v, c, used, n); //考虑取i
166 // }
167 // int getMaxV(){
168 // return maxValue;
169 // }
170 //private:
171 // int maxW; //背包所能承受的重量
172 // int ac;
173 // int maxValue;
174 // int nowValue;
175 //};
176
177 /**//// <summary>调用示例</summary>
178 /// <param name="args"></param>
179 static void Main(string[] args)
180 {
181 Loading l = new Loading(5, 5); // 设置货车的重容和体容
182
183 int n = Convert.ToInt32(Console.ReadLine());
184 TranBill[] tranBills = new TranBill[n];
185 string inStream;
186 for (int i = 0; i < n; i++)
187 {
188 inStream = Console.ReadLine();
189 string[] temp = inStream.Split('\t');
190 tranBills[i].tranID = temp[0];
191 tranBills[i].tranSplitID = int.Parse(temp[1]);
192 tranBills[i].billWeight = float.Parse(temp[2]);
193 tranBills[i].billVolumn = float.Parse(temp[3]);
194 tranBills[i].tranPrice = float.Parse(temp[4]);
195 }
196 List<string> optimizedBills = l.doLoad(tranBills, 0.5f); // 执行最优装载求解
197 Console.WriteLine(l.totalPrice); // 最大托运费
198 foreach (string s in optimizedBills) // 装载序列
199 {
200 Console.Write(s + '\t');
201 }
202 }
203 }
204}
205
206/**//* 示例输入
2075
208tran 1 1 1 100
209tran 2 1 1 100
210tran 3 1 1 100
211tran 4 1 1 100
212tran 5 1 1 100
213*/
1/**//* 0-背包问题扩展
2 * By Flouse@2008年12月9日 17时06分28秒
3 */
4using System;
5using System.Collections.Generic;
6using System.Text;
7
8namespace YourNameSpace // 修改为统一的命名空间
9{
10 /**//// <summary>装车类</summary>
11 public class Loading
12 {
13 /**//// <summary>货车载重量(t)</summary>
14 private int capacity;
15 /**//// <summary>货车体积容量(m3)</summary>
16 private int cubage;
17 /**//// <summary>最优(运费最高)装载清单</summary>
18 private List<string> optimizedBills = new List<string>();
19 public float totalPrice;
20
21 /**//// <summary>运单信息(装载相关)</summary>
22 public struct TranBill
23 {
24 /**//// <summary>托运单号</summary>
25 public string tranID;
26 /**//// <summary>拆单号</summary>
27 public int tranSplitID;
28 /**//// <summary>重量(t),实际运算里四舍五入化整</summary>
29 public float billWeight;
30 /**//// <summary>体积(m3),实际运算里四舍五入化整</summary>
31 public float billVolumn;
32 /**//// <summary>运费</summary>
33 public float tranPrice;
34 };
35
36 /**//// <summary>初始化货车信息</summary>
37 /// <param name="capacity">货车载重量(t)</param>
38 /// <param name="cubage">货车体积容量(m3)</param>
39 Loading(int capacity, int cubage)
40 {
41 this.capacity = capacity;
42 this.cubage = cubage;
43 }
44 ~Loading()
45 {
46 GC.Collect();
47 //?? 释放内存
48 }
49 /**//// <summary>求解最优装车方案</summary>
50 /// <param name="tbs">待运的托运单</param>
51 /// <param name="mode">重量、体积转整模式
52 /// <value>0.0f~1.0f, 当 mode = 0.5f 时,即为四舍五入方式;当 mode = 0.0f 时, 即为最贪婪方式;当 mode = 1.0f 时,即为最保险方式</value>
53 /// <example>(int)(billWeight + mode), (int)(billVolumn + mode)</example>
54 /// </param>
55 /// <returns></returns>
56 public List<string> doLoad(TranBill[] tbs, float mode)
57 {
58 int count = tbs.Length;
59 int[] w = new int[count + 1];
60 int[] v = new int[count + 1];
61 float[] p = new float[count + 1];
62 float[, ,] m = new float[count + 1, capacity + 1, cubage + 1];
63 for (int i = 1; i <= count; i++)
64 {
65 w[i] = (int)(tbs[i - 1].billWeight + mode);
66 v[i] = (int)(tbs[i - 1].billVolumn + mode);
67 p[i] = tbs[i - 1].tranPrice;
68 }
69 Knapsack(p, w, v, count, m);
70 Trackback(m, w, v, tbs);
71 return optimizedBills;
72 }
73
74 private void Trackback(float[, ,] m, int[] w, int[] v, TranBill[] tbs)
75 {
76 int _capacity = capacity, _cubage = cubage, n = tbs.Length;
77 optimizedBills.Clear();
78 for (int i = 1; i < n; i++)
79 {
80 if (m[i, _capacity, _cubage] - m[i + 1, _capacity, _cubage] > 0.000001f)
81 {
82 optimizedBills.Add(tbs[i - 1].tranID + '.' + tbs[i-1].tranSplitID);
83 _capacity -= w[i];
84 _cubage -= v[i];
85 }
86 }
87 if (m[n, _capacity, _cubage] > 0) optimizedBills.Add(tbs[n - 1].tranID + '.' + tbs[n - 1].tranSplitID);
88 }
89
90 /**//// <summary>采用动态规划标记法(0-1背包问题算法)</summary>
91 /// <param name="p"></param>
92 /// <param name="w"></param>
93 /// <param name="v"></param>
94 /// <param name="n"></param>
95 /// <param name="m"></param>
96 private void Knapsack(float[] p, int[] w, int[] v, int n, float[, ,] m)
97 {
98 int wMax = Math.Min(w[n] - 1, capacity);
99 int vMax = Math.Min(v[n] - 1, cubage);
100
101 int i, j, k;
102
103 for (i = 0, j = 0; i <= wMax; i++)
104 for (j = 0; j < cubage; j++)
105 m[n, i, j] = 0;
106 for (j = 0; j <= vMax; j++)
107 for (i = wMax+1; i < capacity; i++)
108 m[n, i, j] = 0;
109
110 for (i = w[n]; i <= capacity; i++)
111 for (j = v[n]; j <= cubage; j++)
112 m[n, i, j] = p[n];
113
114 for (k = n - 1; k > 1; k--)
115 {
116 wMax = Math.Min(w[k] - 1, capacity);
117 vMax = Math.Min(v[k] - 1, cubage);
118
119 for (i = 0, j = 0; i <= wMax; i++)
120 for (j = 0; j < cubage; j++)
121 m[k, i, j] = m[k + 1, i, j];
122 for (j = 0; j <= vMax; j++)
123 for (i = wMax+1; i < capacity; i++)
124 m[k, i, j] = m[k + 1, i, j];
125
126 for (i = w[k]; i <= capacity; i++)
127 for (j = v[k]; j <= cubage; j++)
128 m[k, i, j] = Math.Max(m[k + 1, i, j], m[k + 1, i - w[k], j - v[k]] + p[k]);
129 }
130 m[1, capacity, cubage] = m[2, capacity, cubage];
131 if (capacity >= w[1] && cubage >= v[1]) m[1, capacity, cubage] = Math.Max(m[1, capacity, cubage], m[2, capacity - w[1], cubage - v[1]] + p[1]);
132
133 totalPrice = m[1, capacity, cubage];
134 }
135
136 // 回溯法,示实现
137 //class BackBag{
138 //public:
139 // BackBag(int W){
140 // maxW = W;
141 // ac = maxValue = nowValue = 0;
142 // }
143 // void KnapSack(int v[], int c[], bool used[], int n){ //回溯法
144 // for (int i = 0; i < n; i++){
145 // nowValue += v[i];
146 // ac += c[i];
147 // used[i] = true;
148 // }
149 // search(0, v, c, used, n);
150 // }
151 // void search(int i, int v[], int c[], bool used[], int n){
152 // if (i==n) return;
153 // if (nowValue <= maxValue) return;
154 // if (ac <= maxW){
155 // maxValue = nowValue;
156 // return;
157 // }
158 // nowValue -= v[i];
159 // ac -= c[i];
160 // used[i] = false;
161 // search(i+1, v, c, used, n); //考虑不取i
162 // nowValue += v[i];
163 // ac += c[i];
164 // used[i] = true;
165 // search(i+1, v, c, used, n); //考虑取i
166 // }
167 // int getMaxV(){
168 // return maxValue;
169 // }
170 //private:
171 // int maxW; //背包所能承受的重量
172 // int ac;
173 // int maxValue;
174 // int nowValue;
175 //};
176
177 /**//// <summary>调用示例</summary>
178 /// <param name="args"></param>
179 static void Main(string[] args)
180 {
181 Loading l = new Loading(5, 5); // 设置货车的重容和体容
182
183 int n = Convert.ToInt32(Console.ReadLine());
184 TranBill[] tranBills = new TranBill[n];
185 string inStream;
186 for (int i = 0; i < n; i++)
187 {
188 inStream = Console.ReadLine();
189 string[] temp = inStream.Split('\t');
190 tranBills[i].tranID = temp[0];
191 tranBills[i].tranSplitID = int.Parse(temp[1]);
192 tranBills[i].billWeight = float.Parse(temp[2]);
193 tranBills[i].billVolumn = float.Parse(temp[3]);
194 tranBills[i].tranPrice = float.Parse(temp[4]);
195 }
196 List<string> optimizedBills = l.doLoad(tranBills, 0.5f); // 执行最优装载求解
197 Console.WriteLine(l.totalPrice); // 最大托运费
198 foreach (string s in optimizedBills) // 装载序列
199 {
200 Console.Write(s + '\t');
201 }
202 }
203 }
204}
205
206/**//* 示例输入
2075
208tran 1 1 1 100
209tran 2 1 1 100
210tran 3 1 1 100
211tran 4 1 1 100
212tran 5 1 1 100
213*/