链接
http://poj.org/problem?id=2287
题目大意
你是田忌,你和齐王赛马,你们两个一人出 n n 匹马,如果你的马属性高,就赢了,挣200块钱,如果你输了,就给齐王200块钱,平手不挣钱也不输钱。问你最多挣多少钱
题解
一开始我的思路是用先把两个人的马都排个序,然后田忌的每一匹马都找一只比它弱的马中最强的那只和他比赛。这样做的漏洞在于两匹马属性相同的时候无法决策,在实际对拍时,发现不同情形下应当采取不同的策略以求最大获利。
贪心算法的实质是,每一步都选择最优决策,致使最终的总决策最优,这种算法要求每个局部之间互不影响,即不存在舍弃当前获利会使整体获利更大的情况。
贪心本身就是一种很玄学的算法,有时候它显然是正确的,但是难以用严谨的数学证明去解释它。比如这道题的贪心策略是这样的:
设两个田忌和齐王的马的序列分别为,最大值、最小值分别记为
amax,amin,bmax,bmin
a
m
a
x
,
a
m
i
n
,
b
m
a
x
,
b
m
i
n
当
amax>bmax
a
m
a
x
>
b
m
a
x
时,
amax
a
m
a
x
这匹马对于齐王的阵容来说是不可战胜的,所以让它去击杀齐王最强的马肯定是最合适的,即
amax
a
m
a
x
和
bmax
b
m
a
x
进行对决
当
amax<bmax
a
m
a
x
<
b
m
a
x
时,
bmax
b
m
a
x
这匹马对于田忌的阵容来说时不可战胜的,因此可以找田忌最弱的马
amin
a
m
i
n
当炮灰,
amin
a
m
i
n
和
bmax
b
m
a
x
进行对决
当
amax=bmax
a
m
a
x
=
b
m
a
x
时,难以决定,再比较最小值
若
amin<bmin
a
m
i
n
<
b
m
i
n
,
amin
a
m
i
n
这匹马太弱了,齐王任何一匹马都能把他干掉,这个
200
200
块钱肯定是要折了,不如让他去和齐王最强的马去对决,即
amin
a
m
i
n
和
bmax
b
m
a
x
进行对决
若
amin>bmin
a
m
i
n
>
b
m
i
n
,
bmin
b
m
i
n
这匹马太弱了,田忌随便一匹马都能把他干掉,但是田忌比较强的马还要留着对战更强的对手,因此让
amin
a
m
i
n
和
bmin
b
m
i
n
对决
若
amin=bmin
a
m
i
n
=
b
m
i
n
,那么这个时候可以让
amin
a
m
i
n
和
bmin
b
m
i
n
对决,这样看似比较优,因为让
amin
a
m
i
n
和其它马对决有可能就要舍钱了,但是别忘了,如果让
amin
a
m
i
n
和更强的马对决,剩下的马中
bmin
b
m
i
n
就会成为最弱的马,这
200
200
块钱肯定是可以拿到的,
amin
a
m
i
n
折的
200
200
块最终还是回来了。那么到底采取哪种策略更好?
注意到这种数据:
4
5 4 3 2
5 4 3 2
如果我总是让相等的对决,那么最终的获利是
0
0
,但是如果我让,
4−3
4
−
3
,
3−2
3
−
2
,
2−5
2
−
5
,这样的话,最终获得了
400
400
块钱。
也就是说当
amin=bmin
a
m
i
n
=
b
m
i
n
时,我应该让
amin
a
m
i
n
去和
bmax
b
m
a
x
对决,这样不仅没有折钱,而且把弱的马让了出来,让其它的马有机会发挥自己的实力,避免出现上述样例中的情况。
代码
//贪心
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#define maxn 1010
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
int a[maxn], b[maxn], ans, n;
int read(int x=0)
{
char c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
bool cmp(int x, int y){return x>y;}
void solve()
{
int i, j, p1, p2, p3, p4;
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
p1=p3=1, p2=p4=n;
ans=0;
for(i=1;i<=n;i++)
{
if(a[p1]>b[p3])p1++, p3++, ans+=200;
else if(a[p1]<b[p3])p2--, p3++, ans-=200;
else if(a[p2]>b[p4])p2--, p4--, ans+=200;
else if(a[p2]<b[p4])p2--, p3++, ans-=200;
else ans+=-200*(a[p2]!=b[p3]), p2--, p3++;
}
}
int main()
{
int i;
for(n=read();n;n=read())
{
for(i=1;i<=n;i++)a[i]=read();
for(i=1;i<=n;i++)b[i]=read();
solve();
printf("%d\n",ans);
}
return 0;
}