bzoj4391: [Usaco2015 dec]High Card Low Card(貪心 + 思維)
阿新 • • 發佈:2019-01-01
題目描述:奶牛Bessie和Elsie在玩一種卡牌遊戲。一共有2N張卡牌,點數分別為1到2N,每頭牛都會分到N張卡牌。
遊戲一共分為N輪,因為Bessie太聰明瞭,她甚至可以預測出每回合Elsie會出什麼牌。
每輪遊戲裡,兩頭牛分別出一張牌,點數大者獲勝。
同時,Bessie有一次機會選擇了某個時間點,從那個時候開始,每回合點數少者獲勝。
Bessie現在想知道,自己最多能獲勝多少輪?
輸入格式:第一行:一個整數N。
第2~N+1行:每行一個整數表述Elsie的卡。
輸出格式:一個整數表示答案。
輸入樣例:
4
1
8
4
3
輸出樣例:
3
解析:一眼看到這倒題好像是一個田忌賽馬,但肯定不能直接列舉改規則的時間點。
於是考慮預處理。用pre[i]表示1~i用大牌獲勝的規則最多贏的輪數;suf[i]表示從i~n用小牌獲勝的輪數。
關於pre和suf的預處理,可以用set來實現,每次從set中lower_bound進行尋找,之後刪除即可。
最後的答案便是max{pre[i]+suf[i+1]}。
關於這個貪心的正確性,證明也比較簡單。
因為pre和suf中刪去的數總個數為n,若有一個元素重複,那麼必定有一個元素不被兩個刪去。
設哪個元素為x,都被刪去的數為y,那麼分兩種情況討論。
若x>y,那麼x可以在大的獲勝的規則中替換y。
若x<y,那麼x可以在小的獲勝的規則中替換x。
所以對答案不會造成影響。
程式碼如下:
#include<cstdio> #include<set> using namespace std; const int maxn = 5e4 + 5; int n, vis[maxn << 1], pre[maxn], suf[maxn], a[maxn], ans; set <int> se1, se2; int read(void) { char c; while (c = getchar(), c < '0' || c > '9'); int x = c - '0'; while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x; } int main() { n = read(); for (int i = 1; i <= n; ++ i) { a[i] = read(); vis[a[i]] = 1; } for (int i = 1; i <= n * 2; ++ i) if (!vis[i]) se1.insert(i), se2.insert(-i); for (int i = 1; i <= n; ++ i) { set <int>::iterator it = se1.lower_bound(a[i]); if (it != se1.end()) pre[i] = pre[i - 1] + 1, se1.erase(it); else pre[i] = pre[i - 1]; } for (int i = n; i ; -- i) { set <int>::iterator it = se2.lower_bound(-a[i]); if (it != se2.end()) suf[i] = suf[i + 1] + 1, se2.erase(it); else suf[i] = suf[i + 1]; } for (int i = 0; i <= n; ++ i) ans = max(ans, pre[i] + suf[i + 1]); printf("%d", ans); return 0; }