1. 程式人生 > >2018.10.01 NOIP模擬 卡牌遊戲(貪心)

2018.10.01 NOIP模擬 卡牌遊戲(貪心)

描述

L最近喜歡上了一個卡片遊戲,遊戲規則是: 2個人一共拿2n張卡片,編號1…2n,每個人n張,然後進行n輪出牌,每輪2個人都打一張牌,,點數大的玩家每次獲1分 L可以預測到對方要打牌的順序。 同時,L有一次機會選擇了某個時間點,從那個時候開始,每回合點數少者獲勝。 請你幫助L獲得最大的分數

輸入

第一行是1個整數n 接下來n行表示,對手每次的出牌,根據這些數字,你一定知道了L手上的牌的吧

輸出

1個整數,表示L能獲得最高分數

樣例輸入

4 1 8 4 3

樣例輸出

3

標籤

usaco2015dec

簡單貪心題。 然而考試的時候失了智少討論了一種情況導致gg。 實際上用到了二分圖匹配的思想,L每次找到剛好比當前的牌小一點的出出去,看能匹配幾個。 如何處理? 我們先考慮第一種比分策略。 我們先將L的對手的牌按照權值從大到小排序。 再把L的牌從大到小排序。 然後思考O

(n2)O(n^2)的暴力貪心方法,即每次列舉斷點之後分別求出左右兩側的最優值加起來。 然後這樣的效率令人窒息。 於是考慮優化。 我們先推出沒有斷點的時候的匹配情況(直接用lower_bound)。 然後假設如下圖一樣匹配。 在這裡插入圖片描述 注:藍點的是對手的。 那麼我們每次相當於是從L的序列中彈掉最不優秀的隊尾,從隊首的序列中彈掉對應下標的數。 而這時為了保證決策最優,我們可以把所有藍點向前移動。 這樣對於還沒有刪除的紅點得出的匹配的數量是不減的。 而彈出去的如何匹配呢? 這個我們可以反向維護一個從小到大的一樣的東西來求解。(考試的時候忘了) 事實上具體實現並沒有上面寫得那麼複雜。 直接用set就可以搞定了。 程式碼:

#include<bits/stdc++.h>
#define N 100005
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,a[N],f[N],g[N],tot=0,ans=0;
bool vis[N];
set<int>l,r;
int main(){ n=read(); for(int i=1;i<=n;++i)++vis[(a[i]=read())]; for(int i=1;i<=n*2;++i)if(!vis[i])r.insert(i),l.insert(-i); for(int i=1;i<=n;++i){ set<int>::iterator it=r.upper_bound(a[i]); if(it==r.end())f[i]=f[i-1]; else f[i]=f[i-1]+1,r.erase(*it); } for(int i=n;i;--i){ set<int>::iterator it=l.upper_bound(-a[i]); if(it==l.end())g[i]=g[i+1]; else g[i]=g[i+1]+1,l.erase(*it); } for(int i=0;i<=n;++i)ans=max(ans,f[i]+g[i+1]); printf("%d",ans); return 0; }