OpenJudge Tian Ji -- The Horse Racing

目录

 

Tian Ji -- The Horse Racing

要求:

描述:

输入 :

输出: 

样例输入: 

样例输出:

问题分析:

情况一:

情况二:

情况三:

最终代码:

总结:

其他思路:


Tian Ji -- The Horse Racing

要求:

总时间限制: 5000ms

内存限制: 65536kB

描述:

Here is a famous story in Chinese history. 

That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others. 

Both of Tian and the king have three horses in different classes, namely, regular, plus, and super. The rule is to have three rounds in a match; each of the horses must be used in one round. The winner of a single round takes two hundred silver dollars from the loser. 

Being the most powerful man in the country, the king has so nice horses that in each class his horse is better than Tian's. As a result, each time the king takes six hundred silver dollars from Tian. 

Tian Ji was not happy about that, until he met Sun Bin, one of the most famous generals in Chinese history. Using a little trick due to Sun, Tian Ji brought home two hundred silver dollars and such a grace in the next match. 

It was a rather simple trick. Using his regular class horse race against the super class from the king, they will certainly lose that round. But then his plus beat the king's regular, and his super beat the king's plus. What a simple trick. And how do you think of Tian Ji, the high ranked official in China? 


Were Tian Ji lives in nowadays, he will certainly laugh at himself. Even more, were he sitting in the ACM contest right now, he may discover that the horse racing problem can be simply viewed as finding the maximum matching in a bipartite graph. Draw Tian's horses on one side, and the king's horses on the other. Whenever one of Tian's horses can beat one from the king, we draw an edge between them, meaning we wish to establish this pair. Then, the problem of winning as many rounds as possible is just to find the maximum matching in this graph. If there are ties, the problem becomes more complicated, he needs to assign weights 0, 1, or -1 to all the possible edges, and find a maximum weighted perfect matching... 

However, the horse racing problem is a very special case of bipartite matching. The graph is decided by the speed of the horses -- a vertex of higher speed always beat a vertex of lower speed. In this case, the weighted bipartite matching algorithm is a too advanced tool to deal with the problem. 

In this problem, you are asked to write a program to solve this special case of matching problem.

输入 :

The input consists of up to 50 test cases. Each case starts with a positive integer n ( n<=1000) on the first line, which is the number of horses on each side. The next n integers on the second line are the speeds of Tian's horses. Then the next n integers on the third line are the speeds of the king's horses. The input ends with a line that has a single `0' after the last test case.

输出: 

For each input case, output a line containing a single number, which is the maximum money Tian Ji will get, in silver dollars.

样例输入: 

3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0

样例输出:

200
0
0

问题分析:

这是众所周知的田忌赛马问题,我们先来理一下这个英文题面。田忌和齐王赛马,约定谁赢一局 就会从对手那里赢取200银元. 如果双方平局,则钱数不会发生变化。那么对于给定的马速序列,我们希望知道田忌最多能够赢多少银元,或者说最少会输掉多少银元。

输入有若干组,以0结束;每一组中第一行n代表双方各有多少匹马,接下来的两行分别代表田忌的马的速度与齐王的马的速度,(注意:虽然样例是从大到小给出,但测试时数据并不一定按照从大到小给出)最后输出田忌所赢钱数就可以了。

while(scanf("%d",&n)&&n!=0){
        //输入部分
        cin.get();//因为用的是scarf,所以需要读掉每行最后的回车
        int Tian[n];
        int King[n];
        for(int i=0;i<n;i++){
            scanf("%d",&Tian[i]);
        }
        cin.get();
        for(int i=0;i<n;i++){
            scanf("%d",&King[i]);
        }
        cin.get();
}

一个自然的思路是通过搜索(枚举),遍历所有可能,从而找到收益最大的值。但我们注意到双方的马匹数是0~1000,这个数据还是非常大的,因为遍历所有可能的时间复杂度是O(n!),也就是最多需要进行1000!次枚举,这个时间是我们不能忍受的,即使本题时间要求是5000ms。

所以我们希望找到一些好的算法(贪心),结合历史上的田忌赛马事件,我们可以简单地得出一个粗糙的“结论”:如果我们用自己的劣马耗掉对方的良马,我方的优势就会扩大。那么基于这样一个“结论”,我们可以对这个想法进行精细化讨论和实现。

首先我们需要对双方的马进行排序,以便我们后续查找对应的马。(这里用到了lambda表达式进行从大到小排序,如果不清楚的话,自己写一个比较函数或者默认从小到大排序也可以)

sort(King,King+n,[](int a,int b){return a>b;});//对于传入的int型a和b,返回a>b的bool值
sort(Tian,Tian+n,[](int a,int b){return a>b;});

情况一:

所以我们就来考虑田忌方最差的马,我们希望可以最大化利用这匹马,那么当我们这匹马连对面最差的马都不如的话,直接用它来消耗对面最好的马(这匹马不管面对谁,结果都为负,所以消耗掉对面最好的马就是收益最好的结果) 

田忌赛马历史故事告诉我们的只有以上规则,但我们可以看到这个规则很不完善:我们仍需分类讨论,当我们的最劣马等于对方最劣马时以及优于对方最劣马的情况。

情况二:

如果我方的最劣马优于对方的最劣马,若此时我们仍用此马来消耗对面最优马,这个情况是不好的,因为我们总能找到一个至少不差并且可能更优的局面:

假设我们现在不考虑己方最差马N对方最优马P后得到的最大收益是K,那么用最差马消耗对面最优马的最佳收益就是(K+(N与P比赛结果))*200,那么在该局面下,我方总有一匹马M和对方最差马Q比赛并且获胜(M>=N>Q),那么当我们用N来和Q比,用M来跟P比,其他比赛不变,我们此时的收益为((K-1)+1+(M与P比赛结果))*200,由于M>=N,所以这个结果至少不会差于上一个局面。

所以如果我方的最劣马优于对方的最劣马,我们总是用己方最劣马来和对方最劣马比赛并获胜。

情况三:

如果我方的最劣马刚好和对方最劣马相当时,我们怎么处理?

考虑这两组数据:

2
20 10
15 10
2
15 10
20 10

对于第一组,我们不能用己方最差换对面最好,这样做(一输一胜)的收益是0,小于最大收益(一平一胜)200;但是对于第二组,我们必须用己方最差换对面最好,(一输一胜)收益为0,大于(一平一输)收益为-200.

所以从上述的例子中我们可以发现此时需要针对最优马再进行分类讨论,如果我方最优马比对方更好,直接最优马先赢下这一局,否则就用最劣马去换掉对面最优的。(证明方式类似上面)

最终代码:

所以在上面的分类讨论中,我们已经把情况讨论齐全。

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int n;
    while(scanf("%d",&n)&&n!=0){
        //输入部分
        cin.get();
        int *Tian=new int[n];//田忌
        int *King=new int[n];//齐王
        for(int i=0;i<n;i++){
            scanf("%d",&Tian[i]);
        }
        cin.get();sort(Tian,Tian+n,[](int a,int b){return a>b;});
        for(int i=0;i<n;i++){
            scanf("%d",&King[i]);
        }
        cin.get();sort(King,King+n,[](int a,int b){return a>b;});
        //算法部分
        int Money=0;int KingLast=n-1;int KingFirst=0;int TianFirst=0;
        for(int TianLast=n-1;TianLast>=TianFirst;){
            if(Tian[TianLast]<King[KingLast]){//情况一
                Money--;
                KingFirst++;
                TianLast--;
            }
            else if(Tian[TianLast]>King[KingLast]){//情况二
                KingLast--;
                Money++;
                TianLast--;
            }
            else{//情况三
                if(Tian[TianFirst]<=King[KingFirst]){//如果最优没有对面好,用最后去换
                    if(Tian[TianLast]<King[KingFirst]) Money--;
                    KingFirst++;
                    TianLast--;
                }
                else{//如果己方最优马比对方最优马好
                    Money++;
                    KingFirst++;
                    TianFirst++;
                }
            }
        }
        printf("%d\n",Money*200);
        //释放内存
        delete []Tian;
        delete []King;
    }
}

总结:

 一道经典的贪心算法,最难的就是对各种情况的讨论以及证明,一定切记避免想当然,不然很容易漏掉一些情况。希望这篇文章对您有所帮助。最后求赞求关注😂(对于贪心题目,时空间复杂度不重要,重要的是自己算法的完备性和合理性)

其他思路:

其实不一定需要先考虑最劣马的分类情况,也可以反过来先考虑最优马,但是我觉得没有最劣马讨论直观,故在此没有列出。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值