其他分享
首页 > 其他分享> > CF1367C - Social Distance(构造+贪心+数学规律+普及级)

CF1367C - Social Distance(构造+贪心+数学规律+普及级)

作者:互联网

CF1367C - Social Distance(源地址自⇔CF1367C

Problem

2888b7c718cecf39717abb3ac6306ad8.png

Example

6
6 1
100010
6 2
000000
5 1
10101
3 1
001
2 2
00
1 1
0
1
2
0
1
1
1

6a65ac932f70c49d7d94b0df015ce294.png

tag:

⇔构造性算法、⇔贪心、⇔数学规律、⇔普及级(*1300)

题意:

对于给定的二进制字符串,规定任意两个 \(1\) 之间至少需要间隔 \(k\) 个 \(0\) ,询问最多可以将多少个 \(0\) 变成 \(1\) 。

思路:

将字符串分为三段分别谈论:

  1. 前缀0,其特点是结尾是1,最优的构造方式是 [1+k个0] , [1+k个0] , [……] , [1+k个0] , 【1 …… …… ……】

  2. 后缀0,最优的构造方式是 【…… …… …… 1】 , [k个0+1] , [k个0+1] , [……] , [k个0+1]

  3. 中间部分,其特点是两端是1,故需要额外垫 \(k\) 个 \(0\) ,最优的构造方式是 【…… …… …… 1】 , [k个0+1] , [k个0+1] , [……] , [k个0+1] , 【垫的k个0】 , 【1 …… …… ……】

要额外注意,对于全 \(0\) 的字符串,人为补上 \(k\) 个 \(0\) ,使得其也满足 [1+k个0] , [1+k个0] , [……] , [1+k个0] , [1] , 【补的k个0】

不同的思路:

(来自排行榜前几的大佬)tag:⇔STL与容器

第一遍遍历,使用带自动排序的容器(如 \(set\) )记录下所有 \(1\) 的位置。

第二遍遍历,只要遇到 Str[i]=='0' ,则使用 lower_bound 查找 \(i-k\) 之后的第一个 \(1\) 的位置,若这个 \(1\) 位于 \(i+k\) 之后、或之后没有 \(1\) ,则说明 \(i\) 这个点可以置 \(1\) , ans++ ,并且记录下这个 \(i\) 的位置。

AC代码1:

//A WIDA Project
//Time:15ms,Me:500Kb
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC			optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#define LL			long long
#define IOS()		ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define FOR(i,a,b)	for(int i=(a);i<=(b);i++)
#define FORD(i,a,b)	for(int i=(a);i>=(b);i--)
LL T,n,k,num,ans,flag1,in,out,flag;
string s;
int main(){
	IOS();
	cin>>T;
	while(T-->0){
		ans=0;flag1=0;num=0;in=0;out=0;flag=0;
		cin>>n>>k;
		cin>>s;
		FOR(i,0,n-1){
			if(s[i]=='1'){//找到第一个1
				flag=1;
				in=i;
				break;
			}
		}
		FORD(i,n-1,0){
			if(s[i]=='1'){//找到倒数第一个1
				out=i;
				break;
			}
		}
		
		FOR(i,in,out){//处理中间段
			if(s[i]=='1'){
				if(num-k>0) ans+=(num-k)/(k+1);
				num=0;
			}else{
				num++;
			}
		}
		
		if(flag==0 && n>k+1){//单独讨论:全0
			ans=(n+k)/(k+1);
		}else if(flag==0 && n<=k+1){//单独讨论:全0
			ans=1;
		}else{//处理前后缀0
			ans+=in/(k+1);
			ans+=(n-out-1)/(k+1);
		}
		cout<<ans<<endl;
	}
	return 0;
}

AC代码2:

//A WIDA Project
//Time:46ms,Me:3600Kb
#include<bits/stdc++.h>
using namespace std;
long long T,n,k,ans;
string str;
int main(){
	ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
	while(T-->0){
		ans=0;
		set<int> S;
		cin>>n>>k;
		cin>>str;
		for(int i=0;i<n;i++){
			if(str[i]=='1') S.emplace(i);//记录1
		}
		for(int i=0;i<n;i++){
			if(str[i]=='0'){
				auto it=S.lower_bound(i-k);//查找i-k之后的第一个‘1’
				if((*it)>i+k || it==S.end()){//如果这个‘1’在i+k之后,或者没有找到‘1’
					S.emplace(i);//置‘1’
					ans++;
				}
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

错误次数:【补题】7次

原因:未将计数器 \(num\) 清零。

原因:全 \(0\) 时的边界情况考虑不周。

原因:贸然使用 \(find\) 函数,没有考虑其会输出 \(-1\) ,直接进入数组导致越界,RE。

原因:未考虑中间段连续 \(0\) 的长度可能小于 \(k\) 的值,造成 \(ans\) 值为负。

原因:直接使用 \(in\) 变量和 \(out\) 变量同时为零来判断所给字符是否全部为 \(0\) ,而实际上应当引入新的 \(flag\) 变量单独判断(Hack:5 2 10000。这组数据会被判定为全 \(0\) )。


文 / WIDA
2021.10.14成文
首发于WIDA个人博客,仅供学习讨论


更新日记:
2021.10.14 成文

标签:Distance,WIDA,Social,cin,flag,num,ans,CF1367C,out
来源: https://www.cnblogs.com/WIDA/p/15408431.html