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

题目意思:

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

思路:

伪代码二分原理:

fun(x)

m为区间[l,r]上一个点

如果 p[m]==x,返回m

否则:

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

否则 l=m+1

简单地说:

伪代码只会判断p[m]是否大于x!!!

伪代码只会判断p[m]是否大于x!!!

伪代码只会判断p[m]是否大于x!!!

若大于x就缩短右边界

反正缩短做左边界

而对于稳定点:

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

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

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

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

因为伪代码任取一点m,只会判断p[m]是否大于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为稳定点

在p[1]=1,p[3]=3的基础上,p[2]只能为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,只会判断p[m]是否大于x,

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

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

非稳定点个数>2时同理

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

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

p[1]的位置只能是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;
}
相关推荐
Yunfeng Peng2 小时前
1- 十大排序算法(选择排序、冒泡排序、插入排序)
java·算法·排序算法
liu****2 小时前
负载均衡式的在线OJ项目编写(五)
运维·c++·负载均衡·个人开发
多看书少吃饭2 小时前
前端实现抽烟识别:从算法到可视化
前端·算法
MMjeaty3 小时前
特殊矩阵的压缩存储
算法·矩阵
断剑zou天涯3 小时前
【算法笔记】二叉树递归解题套路及其应用
java·笔记·算法
saber_andlibert4 小时前
【Linux】深入理解Linux的进程(一)
linux·运维·服务器·开发语言·c++
YXXY3136 小时前
算法练习(C++)---双指针
c++
yanqiaofanhua7 小时前
C语言自学--数据在内存中的存储
c语言·开发语言
玖笙&8 小时前
✨WPF编程基础【1.3】:XAML 名称空间
c++·wpf·visual studio