poj2287 Tian Ji -- The Horse Racing

链接

http://poj.org/problem?id=2287

题目大意

你是田忌,你和齐王赛马,你们两个一人出 n n 匹马,如果你的马属性高,就赢了,挣200块钱,如果你输了,就给齐王200块钱,平手不挣钱也不输钱。问你最多挣多少钱

题解

一开始我的思路是用先把两个人的马都排个序,然后田忌的每一匹马都找一只比它弱的马中最强的那只和他比赛。这样做的漏洞在于两匹马属性相同的时候无法决策,在实际对拍时,发现不同情形下应当采取不同的策略以求最大获利。
贪心算法的实质是,每一步都选择最优决策,致使最终的总决策最优,这种算法要求每个局部之间互不影响,即不存在舍弃当前获利会使整体获利更大的情况。
贪心本身就是一种很玄学的算法,有时候它显然是正确的,但是难以用严谨的数学证明去解释它。比如这道题的贪心策略是这样的:
设两个田忌和齐王的马的序列分别为a,b,最大值、最小值分别记为 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 ,但是如果我让54 43 4 − 3 32 3 − 2 25 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值