zoukankan      html  css  js  c++  java
  • [洛谷P1650] 田忌赛马

    贪心难题;总结贪心问题的一般思路

    传送门:$>here<$

    题意

    田忌和齐王各有n匹马,赛马时一一对应。赢+200,输-200,平+0. 问最多多少钱?

    数据范围:$n leq 2000$

    Solution

    如果没有平局

    将齐王和田忌的马都按照速度从大到小排序。然后同时从两方最大的开始考虑。

    设齐王当前最大的马为x,最小的为y;田忌最大的为a,最小的为b;

    若x>a,说明x大于任何田忌的马。此时应当使用b去碰x。证明:如果不使用b,而使用比b更大的马,设为c,去碰x能达到最优解。用c也输,用b也输,用b去反而留出更大的c去赢别的。故使用b也可以达到最优解。

    若x<a,此时应当让a去赢x。证明:如果不使用a,而使用比a更小的马去碰x能达到最优解。那么a肯定碰了个更弱的。若交换必定不会造成损失。故使用a也可以达到最优解。

    决定了这一步,之后就是子问题了。一个模子去解决即可。

    有平局

    和刚才一样,只不过多了一类情况。

    若x=a,那么暂且不能决定——这是一个僵局。我们希望摆脱这一局面,转化为刚才的形式。去寻找看还有什么能一步决定。

    1. y>b,说明b死定了。要死就与齐王最强的同归于尽,证明雷同。

    2. y<b,应当让b去赢。证明同x<a的情况。

    1能够改变x=a的现状,继续做子问题即可。2的话就继续判断尾巴。

    关键问题来了——

    3. y=b

    此时头相等,尾相等。相当棘手。

    我们应当选择令b去碰x。证明:如果b不碰x也能达到最优解。那么意味着

    1. 两头都去碰平。那么如果使两头交叉,不会有所损失。

    2. 一头碰平。以b碰y为例。设d遇到x,那么d的一定输。(-200)那么交换,使x碰b,d碰y(+0)。故没有损失

    3. 两头都不碰平。x碰b更优。

    透过题解看本质

    贪心问题的一般思路

    1. 通过小数据发现规律,猜出贪心策略。

    2. 验证最优子结构性质(即能否DP)

    贪心可以看做DP的一种特殊情况,只不过每一步不需要考虑所有可能情况,而是直接选择最优的。

    3. 验证贪心选择性。一般使用反证法。

    有点像数学归纳法。即证出该策略能够达到最优解。设...能达到最优解,改用该策略不会有损失,故能达到最优解。

    在第一步能够用这种方法达到最优解,然后解子问题。子问题中第一步能达到子问题最优解,然后解子问题的子问题……故最终得到最优解。

    利用常识

    这道题中,利用了田忌赛马的常识帮助我们寻找策略。“赢多少不重要,只要赢就好”这一思想使我们想到了赢的不浪费。

    子问题

    在贪心里,利用子问题去思考依然很重要。

    my code

    注意x碰b的时候要考虑平局。

    /*By DennyQi 2018*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 10010;
    const int MAXM = 20010;
    const int INF = 0x3f3f3f3f;
    inline int Max(const int a, const int b){ return (a > b) ? a : b; }
    inline int Min(const int a, const int b){ return (a < b) ? a : b; }
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    int n,a[2001],b[2001];
    int Win(int h1, int t1, int h2, int t2){
        if(h1 == t1){
            if(a[h1] == b[h2]) return 0;
            if(a[h1] > b[h2]) return 200;
            if(a[h1] < b[h2]) return -200;
        }
        if(a[t1] > b[t2]) return Win(h1,t1-1,h2,t2-1)+200;
        if(a[t1] < b[t2]) return Win(h1+1,t1,h2,t2-1)-200;
        if(a[h1] > b[h2]) return Win(h1+1,t1,h2+1,t2)+200;
        if(a[h1] < b[h2]) return Win(h1+1,t1,h2,t2-1)-200;
        return Win(h1+1,t1,h2,t2-1)-200*(a[h1]!=b[t2]);
    }
    int main(){
        n = read();
        for(int i = 1; i <= n; ++i) a[i] = read();
        for(int i = 1; i <= n; ++i) b[i] = read();
        sort(a+1,a+n+1);
        sort(b+1,b+n+1);
        printf("%d", Win(1,n,1,n));
        return 0;
    }
  • 相关阅读:
    Leetcode: Construct Binary Tree from Preorder and Inorder Traversal
    Leetcode: Flatten Binary Tree to Linked List
    Leetcode: Binary Tree Level Order Transversal II
    Leetcode: Binary Tree Level Order Traversal
    Leetcode: Binary Tree Postorder Transversal
    Leetcode: Binary Tree Inorder Transversal
    Leetcode: Word Break II
    Leetcode: Word Break
    Leetcode: Maximum Subarray
    WDS 三种模式
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/10360080.html
Copyright © 2011-2022 走看看