其他分享
首页 > 其他分享> > CF1707A题解

CF1707A题解

作者:互联网

原题

CF1707A Doremy's IQ


思路概述

题意分析

给定一个长度为 \(n\) 的序列 \(a_i\) 和一个整数 \(q\),对于序列中的每个元素按顺序遍历并选数,每个元素可选可不选,但如果出现选取了 \(a_i>q\) 的情况,\(q\) 减去 \(1\),在保证选取元素数量最多的前提下输出选数情况。

思路分析

最先看题以为是背包,故尝试定义状态并写递推关系式。但是由于是按顺序选数,所以会存在选了当前的 \(a_i>q\),导致往后没法选出最多元素的情况,即有后效性,所以该方法不可行。

通过甚至不用简单分析可以得到一种“次优解”思路,即只选取 \(a_i≤q\) 这种情况的元素。这样可以保证在\(q\) 不减少的前提下选取最多元素。

但是上述思路有一个问题,即部分情况可以先选取若干个 \(a_i>q\) 的元素,因为往后的元素过小而对之后选取元素不造成影响,例如以下数据:

\[\{ a_i\}=\{5,1,1,1,1\},q=4 \]

所以我们可以采取以下策略:对给定的数列,在前半段采取只选取 \(a_i≤q\) 的做法,在后半段采取选取所有元素的做法。只要能恰当求出两种策略的使用区间,就可以选取最多元素。

在此策略下,前半段区间越长,\(q\) 最后的值必然越大,而选取元素是数量必然越少。这种单调性意味着本题可以采用二分答案的算法,在主函数二分出两种选取方法的区间,在检验函数中检验当前划分是否可行。最后使得前半段区间尽可能短,即得到正确答案。


算法实现

关于二分的开闭区间问题

由于本题数据都是整数,所以务必注意二分过程中区间开闭问题。

笔者采用的是左闭右开区间,所以在二分过程中,变量 \(r\) 始终指向区间最右端右边第一个元素,所以在判定时保证 \(l<r\) 并且在调整区间左端点时将 \(l\) 调整为 \(mid+1\),调整右端点时将 \(r\) 调整为 \(mid\)。左开右闭区间同理。

关于检查函数

检验函数编写很简单,但是注意当前划分可行的条件是 \(IQ_{final}≥0\) 而非 \(IQ_{final}>0\)。


AC code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define RI register int
using namespace std;
const int maxn=1e5+10;
int T,n,q,pos;
int a[maxn];
bool ans[maxn];
inline bool judge(int x);
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	for(cin >> T;T;--T)
	{
		cin >> n >> q;
		memset(a,0,sizeof(a));memset(ans,0,sizeof(ans));pos=n+1;
		for(RI i=1;i<=n;++i) cin >> a[i];
		RI l=1,r=n+1;
		while(l<r)
		{
			RI mid=(l+r)>>1;
			if(judge(mid))
			{
				pos=mid;
				r=mid;
			}
			else l=mid+1;
		}
		for(RI i=1;i<pos;++i) ans[i]=(a[i]<=q);
		for(RI i=pos;i<=n;++i) ans[i]=1;
		for(RI i=1;i<=n;++i) printf("%d",ans[i]);
		putchar('\n');
	}
	return 0;
}
inline bool judge(int x)
{
	RI IQ=q;
	for(RI i=x;i<=n;++i) if(a[i]>IQ) --IQ;
	return (IQ>=0);
}

标签:二分,int,题解,元素,选取,IQ,CF1707A,include
来源: https://www.cnblogs.com/frkblog/p/16678473.html