[USACO15DEC]高低卡(白金)High Card Low Card (Platinum)
贪心
题解:
贪心!
从1到n扫一遍,f[i]记录点数大的赢到i时最多能赢几轮
反过来扫一遍,g[i]记录点数小的赢到i是最多能赢几轮
用set来贪心得到f[]和g[]。
这样f[i]+g[i+1]的最大值即为最终答案。
很多人都会提到说可能一张牌在点数大的赢的时候被用了,后面改规则后也被用了,就被重复用了两次。但是其实没有关系。如果一张牌设为a被用了两次,那么就说明手中有一张牌设为b没有被用过。若a>b,那么在点数小的赢那轮可以用b去替代a来赢得胜利;反之亦然。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <set>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 100005;
int n,a[N],f[N],g[N];
multiset<int> s;
bool vis[N<<1];
int main(){
freopen("a.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++){ scanf("%d",a+i); vis[a[i]]=true; }
s.clear();
for(int i=1;i<=n*2;i++) if(!vis[i]) s.insert(i);
for(int i=1;i<=n;i++){
set<int>::iterator it = s.upper_bound(a[i]);
if(it != s.end()){ s.erase(it); f[i]=f[i-1]+1; }
else{ f[i]=f[i-1]; }
}
s.clear();
for(int i=1;i<=n*2;i++) if(!vis[i]) s.insert(i);
for(int i=n;i>=1;i--){
set<int>::iterator it = s.lower_bound(a[i]);
if(it != s.begin()){ it--; s.erase(it); g[i]=g[i+1]+1; }
else{ g[i]=g[i+1]; }
}
// for(int i=1;i<=n;i++) D(f[i]); E;
// for(int i=1;i<=n;i++) D(g[i]); E;
int ans=max(f[n],g[1]);
for(int i=1;i<n;i++) ans=max(ans,f[i]+g[i+1]);
printf("%d\n",ans);
}