【NOIP校内模拟/Usaco2015 dec—High Card Low Card】cardgame

【题意】

L最近喜欢上了一个卡片游戏,游戏规则是: 2 个人一共拿 2n 张卡片,编号 1..2n,每个人 n 张,然后进行 n 轮出牌,每轮 2 个 人都打一张牌,,点数大的玩家每次获 1分。 L可以预测到对方要打牌的顺序。 同时,L有一次机会选择了某个时间点,从那个时候开始,每回合点数少者获胜。
请你帮助 L获得最大的分数

我们用贪心来做

假设规则没有变的时候 对于对方打出的一张牌 L从他的牌里打出一张刚好比这张牌大的牌显然是最优的

假设规则变了的时候 对于对方打出的一张牌 L从他的牌里打出一张刚好比这张牌小的显示是最优的

似乎这个地方也有另一种贪心的方法 对于每一张牌 找一个最大的比他的牌也是可行的 因为这样在变规则后 可以使得剩下的牌更好得分

我们考虑如何实现

我们维护两个数组 front back,front[i]:没变规则时,1~i轮能得到的最大分数 back[i]:变了规则后,i~n轮能得到的最大分数

答案为max{front[i]+back[i+1]}

很多人都会提到说 可能一张牌在点数大的赢的时候被用了,后面改规则后也被用了,就被重复用了两次。

但是其实没有关系。如果一张牌a被用了两次,那么就说明手中有一张牌设为b没有被用过。若a>b,那么在点数小的赢那轮可以用b去替代a来赢得胜利;反之亦然。

最后 数组记得开100000 不然会炸成30分

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,cnt;
int t[N],card[N],sx[N];
int front[N],back[N];
bool used[N];
int find_upper(int x)
{
	int l=0,r=n;
	while(l<=r)
	{
		int m=(l+r)>>1;
		if(card[m]>x)	r=m-1;
		else l=m+1;	
	}
	return l;
}
int find_lower(int x)
{
	int l=0,r=n;
	while(l<=r)
	{
		int m=(l+r)>>1;
		if(card[m]>x)	r=m-1;
		else l=m+1;	
	}
	return l-1;
}
int main()
{
	freopen("cardgame.in","r",stdin);
	freopen("cardgame.out","w",stdout); 
	memset(used,false,sizeof(used));
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>sx[i];
		t[sx[i]]++;
	}
	for(int i=1;i<=2*n;i++)
	{
		if(!t[i])	card[++cnt]=i;
	}
	for(int i=1;i<=n;i++)
	{
		int pos=find_upper(sx[i]);
		while(used[pos]==true)	pos++;	
		if(pos>n)	front[i]=front[i-1];
		else 
		{
			used[pos]=true;
			front[i]=front[i-1]+1;	
		}		
	}
	//for(int i=1;i<=n;i++)	cout<<front[i]<<" ";
	memset(used,false,sizeof(used));
	for(int i=n;i>=1;i--)
	{
		int pos=find_lower(sx[i]);
		while(used[pos]==true)	pos--;
		if(pos<1)	back[i]=back[i+1];
		else {
			used[pos]=true;
			back[i]=back[i+1]+1;
		}
	}
	//for(int i=1;i<=n;i++)	cout<<back[i]<<" ";
	int ans=0;
	for(int i=0;i<=n;i++)
	{
		ans=max(front[i]+back[i+1],ans);
	}
	cout<<ans;
	return 0;
}
posted @ 2018-10-01 15:25  Patrickpwq  阅读(141)  评论(1编辑  收藏  举报