1. 程式人生 > >[洛谷P1650] 田忌賽馬

[洛谷P1650] 田忌賽馬

str 判斷 最大的 沒有 排序。 思考 轉化 發現 排序

貪心難題;總結貪心問題的一般思路

傳送門:$>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;
}

[洛谷P1650] 田忌賽馬