【ACWing】1489. 田忌赛马(配数学证明)

题目地址:

https://www.acwing.com/problem/content/1491/

这是中国历史上的一个著名故事。大约 2300 2300 2300年前,田忌是齐国的一位将军,他喜欢与国王等人赛马。田忌和国王都有三匹不同等级的马----下马、中马、上马。规则是一场比赛要进行三个回合,每匹马进行一回合的较量,单回合的获胜者可以从失败者那里得到 200 200 200银元。比赛的时候,国王总是用自己的上马对田忌的上马,中马对中马,下马对下马。由于国王每个等级的马都比田忌的马强一些,所以比赛了几次,田忌都失败了,每次国王都能从田忌那里拿走 600 600 600银元。田忌对此深感不满,直到他遇到了著名的军事家孙膑,利用孙膑给他出的主意,田忌终于在与国王的赛马中取得了胜利,拿走了国王的 200 200 200银元。其实胜利的方法非常简单,他用下马对战国王的上马,上马对战国王的中马,中马对战国王的下马,这样虽然第一回合输了,但是后两回合都胜了,最终两胜一负,取得了比赛胜利。
在这里插入图片描述
如果田忌活在如今,那么他可能会嘲笑自己当初的愚蠢。重要的是,如果他现在正参加ACM竞赛,他可能会发现赛马问题可以简单地看作是在二分图中找到最大匹配项。在一侧画田忌的马,在另一侧画国王的马,只要田忌的一匹马能够击败国王的一匹马,我们就在这两匹马之间画一条边。然后,赢尽可能多的回合,就变成了在这个二分图中找到最大匹配。如果存在平局,问题会变得更加复杂,他需要为所有可能的边分配权重 0 0 0 1 1 1 − 1 −1 1,并找到最大加权的完美匹配……然而,赛马问题其实是二分图匹配的一种非常特殊的情况。该图由马的速度决定,速度快的总是能击败速度慢的。这种情况下,加权二分图匹配算法就显得大材小用了。在这个问题中,你需要编写一个程序,来解决这一特殊的匹配问题。

输入格式:
输入包含最多 50 50 50组测试数据。每组数据第一行包含一个整数 n n n,表示一方马的数量。第二行包含 n n n个整数,是田忌的马的速度。第三行包含 n n n个整数,是国王的马的速度。输入的最后一行为 0 0 0,表示输入结束。

输出格式:
每组数据输出一个占一行的整数,表示田忌最多可以获得多少银元。

数据范围:
1 ≤ n ≤ 1000 1≤n≤1000 1n1000
马的速度不超过 1000 1000 1000

题目说明不需用二分图匹配的思路来做,我们可以考虑一种贪心策略。直觉上应该从最快或最慢的马来考虑。假设国王派出了最快马,我们分情况考虑:
1、如果这个最快马比田忌的最快马还要快,那么田忌无论怎样都要输掉这轮比赛,所以最优解就是让自己的最慢马去与国王的最快马去比(证明不难,只需将非此操作的最优解中的田忌的与国王最快马比的那匹马与田忌自己的最慢马换一下就也得到了一个最优解,这说明最优解是可以有这条匹配边的);
2、如果国王最快马比田忌最快马要慢,那么从田忌的最快马的角度考虑,它的最大效益应该发挥在用它来击败国王最快马上,因为它反正能击败国王的任一马,所以击败国王最快马才最划算,所以最优解是让自己的最快马去击败国王最快马;
3、如果国王最快马和田忌最快马一样快,那么此时田忌无法确定是否要派最快马出战,事实上出战有可能不是最优解(例如田忌的马的速度数组是 [ 20 , 30 ] [20, 30] [20,30],国王的是 [ 25 , 30 ] [25, 30] [25,30],那么田忌的最优解并不是让自己的最快马去和国王最快马比,这样的总得分是 − 200 -200 200;他应该让自己的最慢马去比,这样总得分是 0 0 0,更优;同样也能举出田忌派最快马是最优解的例子),那么我们可以转而去看两个人的最慢马。
3(1)、如果田忌的最慢马比国王的最慢马快,那么田忌果断要让自己的最慢马去对战国王的最慢马,以发挥他最慢马的效益。证明与上类似;
3(2)、如果田忌的最慢马比国王的最慢马慢,那他的最慢马怎么比都要输,所以可以让它去和国王的最快马去比,最大程度消耗对手。证明与上类似;
3(3)、如果田忌的最慢马与国王的最慢马一样快,那么就有两种考虑,要么让它去消耗国王的最快马,要么让它去和国王最慢马比。我们仔细考虑这两种可能性。首先田忌的这个最慢马不会和别的马去比,如果它和国王的一个非最快也非最慢的马去比的话,那这匹马必输,那不如去和国王最快马比更优,所以田忌最慢马只会和国王的最快或者最慢马比。如果某个最优解里,田忌最慢马是和国王最慢马比的,那么考虑国王最快马是和田忌的哪个马比的,把田忌的那匹马挑出来和国王最慢马比,然后把田忌最慢马和国王最快马比,这样田忌的收益不会变得更差(如果田忌那匹马输了,那么这样交换后田忌有可能免于 − 200 -200 200的输钱,也有可能收益不变;如果田忌那匹马没输,即平手,那么交换后收益没有变化)。总而言之,田忌仍然要把最慢马与国王最快马去比。

上面无论哪一个都能使得题目的规模少 1 1 1,一路递推下去就能得到田忌的最大利润。代码如下:

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;
int n;
int a[N], b[N];

int main() {   
    while (cin >> n, n) {
        for (int i = 0; i < n; i++) cin >> a[i];
        sort(a, a + n);
        for (int i = 0; i < n; i++) cin >> b[i];
        sort(b, b + n);

        int res = 0;
        for (int la = 0, ra = n - 1, lb = 0, rb = n - 1; la <= ra;) {
        	// 国王最快马比田忌最快马快,则田忌派出最慢马;
        	// 国王最快马比田忌最快马慢,则田忌派出最快马;
            if (b[rb] > a[ra]) {
                res -= 200;
                la++, rb--;
            } else if (b[rb] < a[ra]) {
                res += 200;
                ra--, rb--;
            } else {
            	// 国王最快马和田忌最快马一样快,如果国王最慢马比田忌最慢马慢,
            	// 则田忌派出最慢马与国王最慢马比;否则派最慢马与国王最快马比
                if (b[lb] < a[la]) {
                    res += 200;
                    la++, lb++;
                } else {
                    if (a[la] < b[rb]) res -= 200;
                    la++, rb--;
                }
            }
        }

        cout << res << endl;
    }

    return 0;
}

每组数据时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间 O ( 1 ) O(1) O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值