楔子
在我看来,贪心是一种每次都查询最优解的一个算法,可以从你所认为的局部最优解推导出全局最优解,也就是答案。
但是,贪心有一个很麻烦的事情是:你并不确定其正确性,如果你想出来的贪心在你并不确定的情况下码完了,之后,你发现怎么都调不过样例,最后,你发现是你的算法错了……如果,这是一场模拟赛,那么,我估计此时你的心态已经崩溃的差不多了(我犯过至少两次这样的错误……)
所以,我想把贪心的一些常用方法和证明以及例题进行以下总结。
微扰
这种方法是用相邻两项进行交换从而验证两项其中的一个怎么样才会使得最后答案最优,一般用于排序的题目。
我们可以看几道例题:
1. P1417 烹调方案
题目简化:一共有 n n n个事件,总时间为 T T T,每个事件有三个属性, a i ai ai, b i bi bi和 c i ci ci,如果在t时刻完成第 i i i样事件则得到 a i − t ∗ b i ai-t*bi ai−t∗bi的价值,用第 i i i个事件要花去 c i ci ci的时间。求出在总时间内的最大价值。
思路:如果价值是固定的话,那么这道题就是一个非常裸的01背包。那么有如果价值是会变化的话,我们就应该考虑的是如何让价值尽可能的大??
之后,我打了一个测试,我把每一个事件进行正序和倒序的测试,之后,我发现这样子的话,答案是不一样的,所以,我们就可以进行猜测这个问题的答案和每一个事件的顺序是有关系的。之后,我们再进行思考事件的顺序到底应该是什么??
这个时候就用到了我们贪心的思想:微扰。
我们考虑两个事件:x, y;他们的三个属性就是:ax, bx, cx……;他们共同的开始时间我们设为T;
①先
x
x
x后
y
y
y:
x
x
x的结束时间是(
T
+
c
x
T+cx
T+cx),y的结束时间是(
T
+
c
x
+
c
y
T+cx+cy
T+cx+cy);
a
n
s
1
=
a
x
−
(
T
+
c
x
)
∗
b
x
+
a
y
−
(
T
+
c
x
+
c
y
)
∗
b
y
ans1=ax-(T+cx)*bx+ay-(T+cx+cy)*by
ans1=ax−(T+cx)∗bx+ay−(T+cx+cy)∗by
a
n
s
1
=
(
a
x
+
a
y
)
−
(
T
+
c
x
)
∗
b
x
−
(
T
+
c
x
+
c
y
)
∗
b
y
ans1=(ax+ay)-(T+cx)*bx-(T+cx+cy)*by
ans1=(ax+ay)−(T+cx)∗bx−(T+cx+cy)∗by
②先
y
y
y后
x
x
x:
y
y
y的结束时间是:(
T
+
c
y
T+cy
T+cy),
x
x
x的结束时间是(
T
+
c
x
+
c
y
T+cx+cy
T+cx+cy);
a
n
s
2
=
(
a
x
+
a
y
)
−
(
T
+
c
y
+
c
x
)
∗
b
x
−
(
T
+
c
y
)
∗
b
y
ans2=(ax+ay)-(T+cy+cx)*bx-(T+cy)*by
ans2=(ax+ay)−(T+cy+cx)∗bx−(T+cy)∗by
若
x
x
x在
y
y
y之间更优的话,那么
a
n
s
1
>
a
n
s
2
ans1>ans2
ans1>ans2。
a
n
s
1
−
a
n
s
2
>
0
ans1-ans2>0
ans1−ans2>0
−
(
T
+
c
x
)
∗
b
x
−
(
T
+
c
x
+
c
y
)
∗
b
y
>
−
(
T
+
c
y
+
c
x
)
∗
b
x
−
(
T
+
c
y
)
∗
b
y
-(T+cx)*bx-(T+cx+cy)*by>-(T+cy+cx)*bx-(T+cy)*by
−(T+cx)∗bx−(T+cx+cy)∗by>−(T+cy+cx)∗bx−(T+cy)∗by
之后,我们在进行化简:
b
x
∗
(
T
+
c
y
+
c
x
−
T
−
c
x
)
>
b
y
∗
(
T
+
c
x
+
c
y
−
T
−
c
y
)
bx*(T+cy+cx-T-cx)>by*(T+cx+cy-T-cy)
bx∗(T+cy+cx−T−cx)>by∗(T+cx+cy−T−cy)
b
x
∗
c
y
>
b
y
∗
c
x
bx*cy>by*cx
bx∗cy>by∗cx
c
x
b
x
<
c
y
b
y
\frac{cx}{bx}<\frac{cy}{by}
bxcx<bycy
所以,只要我们把事件按照这样子进行排序,之后再做一次01背包即可即可。
code
#include<bits/stdc++.h>
using namespace std;
const int nn=53;
typedef long long ll;
int T, n;
ll f[100003], ans=0;
struct food
{
int a, b, c;
double tt;
}d[nn];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
bool cmp(food x,food y)
{
return x.tt<y.tt;
}
int main()
{
T=read(), n=read();
for(int i=1;i<=n;++i) d[i].a=read();
for(int i=1;i<=n;++i) d[i].b=read();
for(int i=1;i<=n;++i) d[i].c=read(), d[i].tt=d[i].c*1.0/d[i].b;
sort(d+1, d+1+n, cmp);
for(int i=1;i<=n;++i)
for(int j=T;j>=d[i].c;--j)
f[j]=max(f[j], f[j-d[i].c]+d[i].a-1ll*j*d[i].b);
for(int j=0;j<=T;++j) ans=max(ans, f[j]);
cout<<ans<<endl;
return 0;
}
习题:赶吃花的牛