1. 程式人生 > >【2018/10/01測試T1】【WOJ 2687】卡牌遊戲

【2018/10/01測試T1】【WOJ 2687】卡牌遊戲

【題目】

題目描述:

L最近喜歡上了一個卡片遊戲,遊戲規則是: 2 個人一共拿 2n 張卡片,編號 1……2n,每個人 n 張,然後進行 n 輪出牌,每輪 2 個人都打一張牌,點數大的玩家每次獲 1 分

L 可以預測到對方要打牌的順序。

同時,L 有一次機會選擇了某個時間點,從那個時候開始,每回合點數少者獲勝。

請你幫助L獲得最大的分數

輸入格式:

第一行是 1 個整數 n

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

輸出格式:

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

樣例資料:

輸入

4 1 8 4 3

輸出

3

【分析】

對於 30% 資料:

列舉每一個位置作為時間點,設第i個點為分割點。 

田忌賽馬的貪心策略,對於前面 1~i-1 的數字從大到小排序,後面的從小到大排序 

用自己最大的牌與前面最大的比較,如果能勝利則 ans++,且丟掉自己的一張牌 

用自己最小的牌與後面最小的比較,如果能勝利則 ans++,且丟掉自己的一張牌 

對於 100% 的資料:

貪心部分與 30分資料差不多 

關鍵是找分割點,可以用陣列排序,也可以 set,把自己的卡片加入到set,每次拿正好大一點的數,用完刪除

正著做一次,反著做一次,然後選一次每個點作為分割點的得分 

【程式碼】

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
using namespace std;
set<int>l,r;
bool flag[N<<1];
int a[N],g[N],f[N];
int main()
{
//	freopen("cardgame.in","r",stdin);
//	freopen("cardgame.out","w",stdout);
	int n,i,ans=0;
	scanf("%d",&n);
	memset(flag,true,sizeof(flag));
	for(i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		flag[a[i]]=false;
	}
	for(i=1;i<=(n<<1);++i)
	  if(flag[i])
		r.insert(i),l.insert(-i);
	for(i=1;i<=n;++i)
	{
		set<int>::iterator it=r.upper_bound(a[i]);   
		if(it!=r.end())
		{
			r.erase(*it);
			f[i]=f[i-1]+1;
		}
		else  f[i]=f[i-1];
	}
	for(i=n;i>=1;--i)
	{
		set<int>::iterator it=l.upper_bound(-a[i]);
		if(it!=l.end())
		{
			l.erase(*it);
			g[i]=g[i+1]+1;
		}
		else  g[i]=g[i+1];
	}
	for(i=0;i<=n;i++)
	  ans=max(f[i]+g[i+1],ans);
	printf("%d\n",ans);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}