acm-(二分、贪心)2020ICPC·小米 网络选拔赛第二场 A.2020

题面
传送门
考虑二分答案,假设答案为 k k k,那么如何取check呢?我们可以从左到右贪心的选 l 1 , l 2 , l 3 . . . l k l_1,l_2,l_3...l_k l1,l2,l3...lk代表左边最近的 k k k个2的位置,然后对于每个2我们都贪心地选一个右边离它最近的位置的0进行配对,这些配对的0的位置分别是 r 1 , r 2 , r 3 . . . r k r_1,r_2,r_3...r_k r1,r2,r3...rk,很显然满足 l 1 < l 2 < . . . < l k , r 1 < r 2 < . . . r k , l i < r i l_1<l_2<...<l_k,r_1<r_2<...r_k,l_i<r_i l1<l2<...<lk,r1<r2<...rk,li<ri,然后我们再从右边到左边贪心的选 k k k个离右边最近的0,按从右到左的顺序存到 r 2 k , r 2 k − 1 , . . . , r k + 1 r_{2k},r_{2k-1},...,r_{k+1} r2k,r2k1,...,rk+1,对于每个0而言同样选择左边离它最近的2,存到 l 2 k , l 2 k − 1 , . . . , l k + 1 l_{2k},l_{2k-1},...,l_{k+1} l2k,l2k1,...,lk+1中,这样子的话我们可以让 ( l i , r i ) (l_i,r_i) (li,ri) ( l i + k , r i + k ) (l_{i+k},r_{i+k}) (li+k,ri+k)配对形成2020,如果无法形成配对,即满足 r i ≥ l i + k r_i\ge l_{i+k} rili+k l 1 ∼ k , r 1 ∼ k l_{1\sim k},r_{1\sim k} l1k,r1k l k + 1 ∼ 2 k , r k + 1 ∼ 2 k l_{k+1\sim 2k},r_{k+1\sim 2k} lk+12k,rk+12k存在交集的话,说明在答案为 k k k的情况下是无法配对出足够数量的 2020 2020 2020的。

以上贪心显然是正确的。

char s[maxn];
int n,mi[maxn],mx[maxn],vis[maxn];

bool check(int k){
	int tot1=0,tot2=0;
	memset(vis,0,sizeof(int)*n);
	FOR(i,0,n){
		if(s[i]=='2' && tot1<k)mi[++tot1]=i;else if(s[i]=='0' && tot2<tot1)mx[++tot2]=i;
		if(tot1==k && tot2==k)break;
	}
	if(tot1!=k || tot2!=k)return false;
	tot1=0,tot2=0;
	ROF(i,n-1,0){
		if(s[i]=='0' && tot1<k)mx[(k<<1)-(tot1++)]=i;else if(s[i]=='2' && tot2<tot1)mi[(k<<1)-(tot2++)]=i;
		if(tot1==k && tot2==k)break;
	}
	if(tot1 !=k || tot2!=k)return false;
	FOR(i,1,k+1){
		if(mx[i]>=mi[i+k] || vis[mi[i]] || vis[mx[i]])return false;
		vis[mi[i+k]]=vis[mx[i+k]]=1;
	}
	return true;
}
int main(){ 
	while(~scanf("%d",&n)){
		scanf("%s",s);
		int ans=0,l=1,r=n;
		while(l<=r){
			int mid=l+r>>1;
			if(ans==mid || check(mid)){
				ans=mid;
				l=mid+1;
			}else r=mid-1;
		}
		wrn(ans);
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值