最优装载问题/0-1背包问题,最小延迟调度问题,硬币贪心策略
最优装载问题
介绍
最优装载问题:某艘船的载重量为C,每件物品的重量为
w
i
≤
C
w_i≤C
wi≤C,要将尽量多的物品装入到船上,问如何选择而使能装的上船的集装箱个数最多?
设
x
i
=
1
x_i=1
xi=1表示第i个集装箱可以装上船,否则
x
i
=
0
x_i=0
xi=0,则问题可以描述为:
目标函数: m a x ∑ i = 1 n x i max\displaystyle∑_{i=1}^nx_i maxi=1∑nxi
约束条件: ∑ i = 1 n w i x i ≤ C \displaystyle∑_{i=1}^nw_ix_i≤C i=1∑nwixi≤C
x i = 0 , 1 x_i=0,1 xi=0,1 i = 1 , 2 , . . , n i=1,2,..,n i=1,2,..,n
分析:这是一个整数规划问题,也是0-1背包问题的特殊情况。对于0-1背包问题可以使用动态规划算法求解,但是对于这个问题有更好的算法:贪心策略。贪心法:每次都选择最轻的,然后再从剩下的n-1件物品中选择最轻的。
算法策略:把n件物品 从小到大排序,然后根据贪心策略尽可能多的选出前i个物品,直到不能装为止。
代码
注意:输入的重量序列已经是递增的序列。
/*
* @Description:
* @Author: dive668
* @Date: 2021-04-30 22:12:23
* @LastEditTime: 2021-04-30 22:21:55
*/
#include <iostream>
#include <queue>
using namespace std;
//输入:集装箱集合N={1,2...,n},集装箱i的重量wi,i=1,2..n,注意wi已经按升序排好了
//输出:集装箱集合I包含于N
int main()
{
int n;
cout << "集装箱个数:";
cin >> n;
int C;
cout << "集装箱限重:";
cin >> C;
int *w = new int[n + 1];//重量数组
w[0] = 0;
int i;
for (i = 1; i <= n;++i)
{
cout << "输入第" << i << "个集装箱的重量";
cin >> w[i];
}
queue<int> q;
q.push(1);//先把第一个最轻的入队列
int weight = w[1];//先把第一个最轻的加入到当前重量weight中
for (i = 2; i <= n;++i)
{
if(weight+w[i]<=C)
{
weight += w[i];//增加当前重量
q.push(i);//将这个第i件货物入队列
}
}
while(!q.empty())//将装载的物品序列顺序输出
{
cout << q.front();
q.pop();
}
return 0;
}
最小延迟调度问题–客户服务问题
介绍
给定等待服务的客户的集合
A
=
1
,
2
,
.
.
.
n
A={1,2,...n}
A=1,2,...n,预计对客户
i
i
i的服务时间是
t
i
t_i
ti,该客户希望完成的时间是
d
i
d_i
di,即
T
=
<
t
1
,
t
2
,
.
.
.
,
t
n
>
T=<t_1,t_2,...,t_n>
T=<t1,t2,...,tn>,
D
=
<
d
1
,
d
2
,
.
.
,
d
n
>
D=<d_1,d_2,..,d_n>
D=<d1,d2,..,dn>。如果对客户i的服务在
d
i
d_i
di之前结束,那么对客户i的服务没有延迟;如果在di之后结束,那么这个服务就被延迟了,延迟的时间等于该服务结束时间减去
d
i
d_i
di,假设
t
i
,
d
i
t_i,d_i
ti,di都是正整数,一个调度是函数
f
:
A
→
B
f:A→B
f:A→B,其中
f
(
i
)
f(i)
f(i)是对客户i的服务开始时间,要求所有区间
(
f
(
i
)
,
f
(
i
)
+
t
i
)
(f(i),f(i)+t_i)
(f(i),f(i)+ti)互不重叠。一个调度f的最大延迟是所有客户延迟时间的最大值。
例如:
客户集: A = 1 , 2 , 3 , 4 , 5 A={1,2,3,4,5} A=1,2,3,4,5
服 务 时 间 集 : T = < 5 , 8 , 4 , 10 , 3 > 服务时间集:T=<5,8,4,10,3> 服务时间集:T=<5,8,4,10,3>
预 期 服 务 完 成 时 间 : D = < 10 , 12 , 15 , 11 , 20 > 预期服务完成时间:D=<10,12,15,11,20> 预期服务完成时间:D=<10,12,15,11,20>
那么对于调度:
f 1 : 1 , 2 , 3 , 4 , 5 → N f_1:{1,2,3,4,5}→N f1:1,2,3,4,5→N
f 1 ( 1 ) = 0 , f 1 ( 2 ) = 5 , f 1 ( 3 ) = 13 , f 1 ( 4 ) = 17 , f 1 ( 5 ) = 27 f_1(1)=0,f_1(2)=5,f_1(3)=13,f_1(4)=17,f_1(5)=27 f1(1)=0,f1(2)=5,f1(3)=13,f1(4)=17,f1(5)=27
客户1,2,3,4,5的延迟分别是0,1,2,16,10;最大为max:16。
延迟时间的计算:第二件事情的开始时间减去第一件事情的要求截止时间,如果为正值,那么就是延迟了,如果为负值,就是没延迟。
调度问题的建模:
给定
A
=
<
1
,
2
,
.
.
.
,
n
>
,
T
=
<
t
1
,
t
2
,
.
.
.
,
t
n
>
,
D
=
<
d
1
,
d
2
,
.
.
.
,
d
n
>
A=<1,2,...,n>,T=<t_1,t_2,...,t_n>,D=<d_1,d_2,...,d_n>
A=<1,2,...,n>,T=<t1,t2,...,tn>,D=<d1,d2,...,dn>,求具有最小延迟的调度。
即求函数f:A→N使得
min
f
{
max
i
∈
A
{
f
(
i
)
+
t
i
−
d
i
}
}
\displaystyle\min_f\{\displaystyle\max_{i∈A}\{f(i)+t_i-d_i\}\}
fmin{i∈Amax{f(i)+ti−di}}
∨
i
,
j
∈
A
,
i
≠
j
,
f
(
i
)
+
t
i
≤
f
(
j
)
或
者
f
(
j
)
+
t
j
≤
f
(
i
)
∨i,j∈A,i≠j,f(i)+t_i≤f(j)或者f(j)+t_j≤f(i)
∨i,j∈A,i=j,f(i)+ti≤f(j)或者f(j)+tj≤f(i)
考虑贪心算法:按照截至时间
d
i
d_i
di从小到大选择任务,在安排时不留空闲时间。
/*
* @Description:
* @Author: dive668
* @Date: 2021-04-30 23:40:37
* @LastEditTime: 2021-05-01 00:08:01
*/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//输入A,T,D
//输出f
struct customer
{
int a;//客户序号
int t;//客户服务时间
int d;//客户要求截至时间
};
bool cmp(customer c1,customer c2)
{
return c1.d <= c2.d;
}
int main()
{
//排序A使得d1<=d2<=d3...<=dn
int n;
cout << "input the number:";
cin >> n;
vector<customer> c;
vector<customer>::iterator p;
customer t;//临时结构体变量,用于压入容器
int i;
for (i = 0; i < n;++i)
{
cout << "客户号:";
cin >> t.a;
cout << "服务时间:";
cin >> t.t;
cout << "要求截至时间:";
cin >> t.d;
c.push_back(t);
}
sort(c.begin(), c.end(), cmp);
int f[n+1];
f[0] = 0;
i = 1;
for (p=c.begin();p!=c.end();++p)
{
f[i] = f[i - 1] + p->t;
++i;
}
cout << "最终截至时间"<<f[n];
}
经过证明,没有逆序,没有空闲时间的调度具有相同的最大延迟。
对贪心法得不到最优解的情况的处理
方法主要有两个:
一是对输入做分析,指出输入在满足哪些条件下贪心法是正确的,而且判定这些条件的时间比较少,至多不超过算法本身的运行时间。
二是分析贪心法的误差,确定它对所有的输入实例得到的解(近似解)与最优解的误差至少有多大。
典型应用实在找零钱问题上,此处仅给出题干:
设有n种硬币,其重量分别为 w 1 , w 2 , . . . , w n w_1,w_2,...,w_n w1,w2,...,wn,币值分别为 v 1 = 1 , v 2 , . . . , v n v_1=1,v_2,...,v_n v1=1,v2,...,vn。现在需要用这些硬币付款的总钱数是Y,问:如何选择这些硬币而使得付钱的硬币总重最轻?
问题建模:
不妨设币值和钱数都为正整数,令选用第i种硬币的数目是 x i x_i xi,那么这个那么这个问题可以建模如下:
m i n { ∑ i = 1 n w i x i } min\{\displaystyle∑_{i=1}^nw_ix_i\} min{i=1∑nwixi}
∑ i = 1 n v i x i = Y \displaystyle∑_{i=1}^nv_ix_i=Y i=1∑nvixi=Y
x i ∈ N , i = 1 , 2... n x_i∈N,i=1,2...n xi∈N,i=1,2...n
可以用动态规划算法求解:采用背包问题的第二种形式的解法。
递推方程:
F
k
+
1
(
y
)
=
min
0
⩽
x
k
+
1
⩽
y
w
k
(
F
k
(
y
−
v
k
+
1
∗
x
k
+
1
)
+
w
k
+
1
∗
x
k
+
1
)
,
k
=
1
,
2
,
.
.
.
,
n
−
1
,
y
=
0
,
1
,
.
.
.
Y
F_{k+1}(y)=\displaystyle\min_{0⩽x_{k+1}⩽\frac{y}{w_k}}({ F_k(y−v_{k+1}*x_{k+1}) + w_{k+1} *x_{k+1}}),k=1,2,...,n-1,y=0,1,...Y
Fk+1(y)=0⩽xk+1⩽wkymin(Fk(y−vk+1∗xk+1)+wk+1∗xk+1),k=1,2,...,n−1,y=0,1,...Y
起始条件:
F
1
(
y
)
=
w
1
[
y
/
v
1
]
=
w
1
y
F_1(y)=w_1[y/v_1]=w_1y
F1(y)=w1[y/v1]=w1y y=0,1,…,Y
时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)
贪心法解:先选"单位价值重量最小的硬币"。
不妨设:
w
1
v
1
≥
w
2
v
2
≥
.
.
.
≥
w
n
v
n
\frac{w_1}{v_1}≥\frac{w_2}{v_2}≥...≥\frac{w_n}{v_n}
v1w1≥v2w2≥...≥vnwn
根据贪心法进行选择时,应该尽可能使用标号大的硬币。
设允许使用前k种零钱,总钱数为y时贪心法得到的总重为
G
k
(
y
)
G_k(y)
Gk(y),则有如下递推公式:
G
k
(
y
)
=
w
k
[
y
v
k
]
+
G
k
−
1
(
y
m
o
d
v
k
)
G_k(y)=w_k[\frac{y}{v_k}]+G_{k-1}(y mod v_k)
Gk(y)=wk[vky]+Gk−1(ymodvk)k=2,3,…,n
G
1
(
y
)
=
w
1
[
y
v
1
]
=
w
1
y
G_1(y)=w_1[\frac{y}{v_1}]=w_1y
G1(y)=w1[v1y]=w1y y=0,1…Y
上述递推公式的含义是:可选硬币是标号为1,2,…,k的硬币时,第k种硬币的个数至多可以用
[
y
v
k
]
[\frac{y}{v_k}]
[vky]个,按照上面的贪心策略,要尽可能使用第k种硬币,因此
x
k
=
[
y
v
k
]
x_k=[\frac{y}{v_k}]
xk=[vky]。这些硬币的重量是
w
k
[
y
v
k
]
w_k[\frac{y}{v_k}]
wk[vky]。剩下的钱数时
y
−
v
k
[
y
v
k
]
y-v_k[\frac{y}{v_k}]
y−vk[vky],这是在尽量使用标号为k的投硬币付款所剩的不足
v
k
v_k
vk的余款。即
y
m
o
d
v
k
y mod v_k
ymodvk。这部分钱只能用更小币值的零钱付款,也就是说只能使用标号为1,2,…,k-1的硬币,于是得到一个子问题。使用贪心法继续求解这个子问题,得到总重量是
G
k
−
1
(
y
m
o
d
v
k
)
G_{k-1}(ymodv_k)
Gk−1(ymodvk)。把这个重量与标号为k的硬币重量加起来就是最终的总重量。由于1号硬币的价值是1,而要付款的钱数是y,于是需要y枚硬币,因此硬币重量就是
w
1
y
w_1y
w1y。这就是递推关系的初值
G
1
(
y
)
G_1(y)
G1(y)。
使用上述递推公式,可以从最大的硬币币值开始,找到第一个小于或等于y的
v
k
v_k
vk,计算
x
k
=
[
y
v
k
]
x_k=[\frac{y}{v_k}]
xk=[vky]。剩下的钱数
y
1
=
y
−
v
k
[
y
v
k
]
y1=y-v_k[\frac{y}{v_k}]
y1=y−vk[vky]。与上一步类似,继续使用贪心法对
y
1
y_1
y1进行类似处理,…,直到剩余钱数
y
t
y_t
yt小于
v
2
v_2
v2或者
y
t
y_t
yt=0为止,令
x
1
=
y
t
x_1=y_t
x1=yt,算法结束。
本文介绍了最优装载问题和最小延迟调度问题。最优装载问题通过贪心策略解决,每次选择最轻的物品装船,直到无法再装。最小延迟调度问题中,要求服务开始时间最小化延迟,同样可以采用贪心策略,按照截至时间从小到大安排服务。此外,还讨论了贪心法在某些情况下可能无法得到最优解的情况,并举例说明了找零钱问题的动态规划和贪心解法。
9628

被折叠的 条评论
为什么被折叠?



