【dp 贪心】bzoj4391: [Usaco2015 dec]High Card Low Card
巧妙的貪心
Description
Bessie the cow is a huge fan of card games, which is quite surprising, given her lack of opposable thumbs. Unfortunately, none of the other cows in the herd are good opponents. They are so bad, in fact, that they always play in a completely predictable fashion! Nonetheless, it can still be a challenge for Bessie to figure out how to win.
Bessie and her friend Elsie are currently playing a simple card game where they take a deck of 2N cards, conveniently numbered 1…2N, and divide them into N cards for Bessie and N cards for Elsie. The two then play NN rounds, where in each round Bessie and Elsie both play a single card. Initially, the player who plays the highest card earns a point. However, at one point during the game, Bessie can decide to switch the rules so that for the rest of the game, the player who plays the lowest card wins a point. Bessie can choose not to use this option, leaving the entire game in "high card wins" mode, or she can even invoke the option right away, making the entire game follow the "low card wins" rule.
Given that Bessie can predict the order in which Elsie will play her cards, please determine the maximum number of points Bessie can win.
奶牛Bessie和Elsie在玩一種卡牌游戲。一共有2N張卡牌,點數分別為1到2N,每頭牛都會分到N張卡牌。
游戲一共分為N輪,因為Bessie太聰明了,她甚至可以預測出每回合Elsie會出什么牌。
每輪游戲里,兩頭牛分別出一張牌,點數大者獲勝。
同時,Bessie有一次機會選擇了某個時間點,從那個時候開始,每回合點數少者獲勝。
Bessie現在想知道,自己最多能獲勝多少輪?
Input
The first line of input contains the value of N (2≤N≤50,000).
The next N lines contain the cards that Elsie will play in each of the successive rounds of the game. Note that it is easy to determine Bessie's cards from this information.
Output
Output a single line giving the maximum number of points Bessie can score.
題目分析
先從約束最少的情況開始考慮。
?
游戲規則不改變
如果每一輪都是點數大/小的人獲勝,顯然一次$O(n)$的貪心就可以了。類似于田忌賽馬的道理。
?
然后從最基礎的暴力考慮起。
?
第一個$n^2$想法
枚舉$n$次斷點,對于斷點的兩邊分開貪心。這里的貪心思路是上面那種,用最近滿足條件的來匹配,如果沒有滿足的匹配,則用最差的匹配之。
這里會出現一個問題:按照這種貪心思路,前一部分貪完之后把一些最小的數用掉了。
對于前一部分來說,這些最小的的確沒什么用;但是對于后一部分來說,它需要的就是這些小的數。
換句話說就是“好心沒好報”,后一部分并不買前一部分貪心后的帳。
?
第二個$n^2$想法
Bessie手上的牌只有$n$張,也就是說她最多得分就是$n$。
那我們感性理解一下,把她出牌得分序列看作是一個01串。這里有很普通但是很重要的一點:每張牌最多對答案貢獻1。
于是這保證了我們可以先不匹配一些回合,轉而進行后面操作的正確性。
然后顯然時間復雜度是很不對的(因為要判斷最近滿足狀態所以還要帶一個log),于是只有34分
1 #include<bits/stdc++.h> 2 const int maxn = 50035; 3 4 int n,a[maxn],b[maxn],ans; 5 int vis[maxn]; 6 bool f[maxn<<1]; 7 8 inline int read() 9 { 10 char ch = getchar(); 11 int num = 0; 12 bool fl = 0; 13 for (; !isdigit(ch); ch = getchar()) 14 if (ch=='-') fl = 1; 15 for (; isdigit(ch); ch = getchar()) 16 num = (num<<1)+(num<<3)+ch-48; 17 if (fl) num = -num; 18 return num; 19 } 20 int main() 21 { 22 register int tot,i,k,tt,cg; 23 n = read(); 24 for (i=1; i<=n; i++) 25 a[i] = read(), f[a[i]] = 1; 26 for (i=1; i<=2*n; i++) 27 if (!f[i]) b[++b[0]] = i; 28 for (k=0; k<=n; k++) 29 { 30 tot = 0; 31 for (i=1; i<=n; i++) 32 { 33 cg = i > k?-1:1; 34 tt = std::lower_bound(b+1, b+n+1, a[i])-b+cg; 35 if (cg==1) tt--; 36 for (; tt>=1&&tt<=n; tt+=cg) 37 if (vis[tt]!=k){ 38 vis[tt] = k, tot++; 39 break; 40 } 41 if (tot+n-i+1 < ans) break; 42 } 43 ans = tot>ans?tot:ans; 44 } 45 printf("%d\n",ans); 46 return 0; 47 }?
?
$nlogn$的想法
回顧一下前兩個$n^2$的思路,想必很顯然的一點是我們可以dp地處理$f[i]$和$g[i]$分別表示從$i$開始向前/向后的最大得分。
對,問題就是出在重復上,這兩個最優方案是有重疊的。所以這題不能分類在動態規劃里。
深入地剖析一下這個重復的特點,注意到一個事實是如果有重復,則一定會有多余數字。
有多余數字會發生很有趣的事情:假設重復的數字是$k$,$a<k<b$且$a,b$多余,那么$a$可以在斷點之后替代$k$;$b$可以在斷點之前替代$k$。
然后就顯然正確了。
?
1 #include<bits/stdc++.h> 2 const int maxn = 50035; 3 4 int n,a[maxn],b[maxn],ans; 5 int f[maxn],g[maxn]; 6 bool vis[maxn],mp[maxn<<1]; 7 8 inline int read() 9 { 10 char ch = getchar(); 11 int num = 0; 12 bool fl = 0; 13 for (; !isdigit(ch); ch = getchar()) 14 if (ch=='-') fl = 1; 15 for (; isdigit(ch); ch = getchar()) 16 num = (num<<1)+(num<<3)+ch-48; 17 if (fl) num = -num; 18 return num; 19 } 20 int main() 21 { 22 n = read(); 23 for (int i=1; i<=n; i++) a[i] = read(), mp[a[i]] = 1; 24 for (int i=1; i<=2*n; i++) 25 if (!mp[i]) b[++b[0]] = i; 26 for (int i=1; i<=n; i++) 27 { 28 int tt = std::upper_bound(b+1, b+n+1, a[i])-b; 29 bool fl = 0; 30 for (; tt<=n; tt++) 31 if (!vis[tt]){ 32 fl = 1, vis[tt] = 1; 33 break; 34 } 35 f[i] = f[i-1]; 36 if (fl) f[i]++; 37 } 38 memset(vis, 0, sizeof vis); 39 for (int i=n; i>=1; i--) 40 { 41 int tt = std::lower_bound(b+1, b+n+1, a[i])-b-1; 42 bool fl = 0; 43 for (; tt; tt--) 44 if (!vis[tt]){ 45 fl = 1, vis[tt] = 1; 46 break; 47 } 48 g[i] = g[i+1]; 49 if (fl) g[i]++; 50 } 51 for (int i=0; i<=n; i++) 52 ans = ans < f[i]+g[i+1]?f[i]+g[i+1]:ans; 53 printf("%d\n",ans); 54 return 0; 55 }?
?
?
END
轉載于:https://www.cnblogs.com/antiquality/p/9159505.html
總結
以上是生活随笔為你收集整理的【dp 贪心】bzoj4391: [Usaco2015 dec]High Card Low Card的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XML序列化和反序列化 以及相关类的
- 下一篇: Andorid获取状态栏高度