[洛谷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; }
[洛谷P1650] 田忌賽馬