描述

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)。
然后假设如下图一样匹配。
2018.10.01 NOIP模拟 卡牌游戏(贪心)
注:蓝点的是对手的。
那么我们每次相当于是从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;
}

相关文章: