Codeforces Round 1052 (Div. 2) C. Wrong Binary Searchong Binary Search

题目意思:

给一个二进制字符串,字符串下标从1开始,如果si为1则i为稳定点,反正为不稳定点,需要我们依据伪代码构造一个长度为n的排序,使得排序中的每个数都满足对应s中的稳定点与不稳定点。

思路:

伪代码二分原理:

fun(x)

m为区间l,r上一个点

如果 pm==x,返回m

否则:

如果区间右端点的值大于pm,r=m-1

否则 l=m+1

简单地说:

伪代码只会判断pm是否大于x!!!

伪代码只会判断pm是否大于x!!!

伪代码只会判断pm是否大于x!!!

若大于x就缩短右边界

反正缩短做左边界

而对于稳定点:

当且仅当find(x)==x时,x才为稳定点,也就是px=x

若find(x)!=x,或未定义,那么x都是不稳定点,也就是px!=x

要使得i点为稳定点,首先必须保证pi=i;

之后就要让伪代码能搜到结点i

因为伪代码任取一点m,只会判断pm是否大于x,如果比i大就收缩右端点

如果比i小就收缩左端点

因此只要i在区间l,r上就必须保证l,i-1上的数都小于i,i+1,r上的数都大于i

这样就可以使得无论在区间上取到哪个数,无论怎么收缩,i都包含在收缩后的边界内,直至搜到i

然而在所有的稳定点必然是按照从小到大排列的基础上:

1.相邻稳定点之间:如果有非稳定点,则非稳定点个数一定>=2,即2相邻稳定点之间距离=2时无效

证:

如果非稳定点个数=1,举例:s=101的情况下,1为稳定点,2为不稳定点,3为稳定点

在p1=1,p3=3的基础上,p2只能为2

那么p=123,结点2就成了稳定点,与s=101不符

如果非稳定点个数>=2,如非稳定点个数=2,举例:s=1001,1为稳定点,2为不稳定点,3为不稳定点 4为稳定点

下标区间2,3只要做到比1大,比4小即可,那么 p=1324 同样可以保证 1,4稳定,2,3不稳定

因为伪代码任取一点m,只会判断pm是否大于x,

区间上除1之外的所有点都是比1大的,所以在搜索1时只会不断收缩右边界,直至搜到1

区间上除4之外的所有点都是比4大的,所以在搜索4时只会不断收缩左边界,直至搜到4

非稳定点个数>2时同理

2.最小稳定点之前:如果有非稳定点,则非稳定点个数一定>=2,即第一个稳定点为2时无效

如果非稳定点个数=1,举例:s=011的情况下,1为不稳定点,2为稳定点,3为稳定点

p1的位置只能是1,此时i就成了稳定点,与s不符

如果非稳定点个数>=2,如非稳定点个数=2,举例:s=0011,1为不稳定点,2为不稳定点,3为稳定点 4为稳定点

p可以为1234,也可以为2134;当p=2134时,因为所有数都比3,4小所以会不断收缩左边界

非稳定点个数>2时同理

3.最大稳定点之后:如果有非稳定点,则非稳定点个数一定>=2,即最后一个稳定点为n-1时无效

证明过程同2.

在上述推导过程中不难看出,在构造排序时,可以利用稳定点将整个排序拆分成若干子区间

在子区间上逆序构造排序即可

AC代码:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;
vector<int> a;//存储所有稳定点
vector<int> p;
void build(int l, int r) {//构造函数
	int len = r - l + 1;
	if (len % 2 == 0) {
		for (int i = r;i >= l;i--) {
			p.push_back(i);
		}
	}
	else {
		int k = (r + l) / 2;
		for (int i = r;i >= l;i--) {
			if (i != k) {
				p.push_back(i);
			}
		}
		p.push_back(k);
	}
}
int main() {
	int t;
	cin >> t;
	getchar();
	while (t--) {
		a.clear();
		p.clear();
		int n;
		cin >> n;
		getchar();
		string s;
		cin >> s;
		a.push_back(0);
		for (int i = 0;i < n;i++) {
			if (s[i] == '1') {
				a.push_back(i + 1);
			}
		}
		if (a.size() == 1) {//没有稳定点
			cout << "YES" << endl;
			for (int i = n;i >= 1;i--) {
				cout << i << " ";
			}
			cout << endl;
		}
		else if (a.size() == n + 1) {//所有点都是稳定点
			cout << "YES" << endl;
			for (int i = 1;i <= n;i++) {
				cout << i << " ";
			}
			cout << endl;
		}
		else {
			bool flag = 1;
			int ll = a[1];//第一个稳定点
			int rr = a.back();//最后一个稳定点
			if (ll == 2 || rr == n - 1) {
				cout << "NO" << endl;
				continue;
			}
			for (int i = 1;i < a.size();i++) {
				if (a[i] - a[i - 1] == 2) {//稳定点之间距离=2且不为0
					flag = 0;
					break;
				}
			}

			if (!flag) {
				cout << "NO" << endl;
			}
			else {	
				cout << "YES" << endl;
				//  构造排序
				for (int i = 1;i < a.size();i++) {
					int l = a[i - 1] + 1;
					int r = a[i] - 1;
					if (l < r) {
						build(l, r);
					}
					p.push_back(a[i]);
				}

				if (a.back() < n) {//处理末尾
					int l = a.back() + 1;
					int r = n;
					build(l, r);
				}
				for (int i : p) {
					cout << i << " ";
				}
				cout << endl;
			}
		}
	}
	return 0;
}
相关推荐
王维同学7 分钟前
[自学][Windows C++]RunOnceEx 注册表分组键的安全遍历
c++·windows·安全·开源
战族狼魂1 小时前
高频面试题精选:分治与AI Agent架构
人工智能·算法·大模型·大语言模型
李剑一1 小时前
你用过网易的将军令嘛?它底层实现账号保护的原理相当简单粗暴
算法
Scott9999HH1 小时前
告别流量波动玄学!从法拉第电磁定律到底层 C/C++ 流量累积算法,深度解密工业级电磁流量计选型与开发
c语言·c++·算法
脱胎换骨-军哥2 小时前
C++/Rust无缝互操作:混合系统新常态
开发语言·c++·rust
天空'之城2 小时前
C 语言工业级通用组件手写 06:固定块内存池
c语言·嵌入式开发·内存池·底层优化
香辣牛肉饭2 小时前
【算法】动态规划 最长公共子序列(LCS)
经验分享·笔记·算法·动态规划
.徐十三.3 小时前
一篇文章看到最短路径——Dijkstra算法
算法
十月的皮皮3 小时前
stm20260720-从新手 C 到量产 STM32 工程:程序设计推导指南
c语言·开发语言·stm32·stm32cubemx·hal库