简单的填数「贪心」

题目描述

题库后缀为contest/138/problem/3

思路分析

我太爱贪心题了,我上辈子一定是道贪心题

  • 题意是要求的序列是每个数都只能和前面的数相等,或者比前面的数大 \(1\),且每个数最少出现 \(2\) 次,最多出现 \(5\) 次。
  • 将所有不是 \(0\) 的数放进一个结构体里,两两之间填数,考虑如何填可以即合法,又能保证答案最优
  • 对于两个非零数之间,我们填数的大小范围就是这两个数之内,可以包括这两个数,为了使答案最优,我们每一次填够两个,如果下个数不超过可填的数的范围就去填下一个,否则接着填这个数。但这时候如果遇到两个数之间有很多零,很有可能会出现一个数被填的超过了 \(5\) 次的情况,所以需要对前面的数修改。类似于,记录每个数第一次和最后一次出现的位置,就可以快速修改,如果需要修改的数已经出现了 \(5\) 次,就让前面的前面数也改,这样递归下去,最后让原来需要修改的可以修改。
  • 最后按题目要求 check 一下就好了
  • 还有挺多细节的,就不一一细说了

\(Code\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define R register
#define N 200010
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,b[N],cnt[N],tot;
int begin[N],end[N];
bool flag[N];
struct data{
	int num,pos;
}a[N];
void rechoose(int x){//修改
	//puts("???");
	if(x==0)return;
	if(cnt[x]<5){//直接改
		if(!flag[end[x]+1]){
			end[x]++;
			begin[x+1]++;
			b[end[x]]--;
			cnt[x]++,cnt[x+1]--;
		}
		return;
	}
	else {//递归处理,让x可以修改
		rechoose(x-1);
		if(cnt[x]<5){
			if(!flag[end[x]+1]){
				end[x]++;
				begin[x+1]++;
				b[end[x]]--;
				cnt[x]++,cnt[x+1]--;
			}
			return;
		}
	}
}
void get_num(int l,int r,int mn,int mx){//填数
	b[l] = mn,b[r] = mx;
	int now = mn;
	for(R int i = l+1;i < r;i++){
		if(cnt[now]>=2&&now<mx){
			if(r==n+1&&n-i+1<2){//这里特判一下,否则会出现1 2 2 3这种情况
				cnt[now]++;
				b[i] = now;
				if(cnt[now]>5)rechoose(now-1);
				continue;
			}
			end[now] = i-1;
			now++;
			cnt[now]++;
			begin[now] = i;
			b[i] = now;
			continue;
		}
		cnt[now]++;
		b[i] = now;
		if(cnt[now]>5)rechoose(now-1);
	}
	if(r==n+1)return;
	cnt[b[r]]++;
	if(cnt[b[r]]>5)rechoose(b[r]-1);
	if(now==mx-1)begin[mx] = r;
}
bool check(){
	int now = 0,cnt = 0;
	for(R int i = 1;i <= n;i++){
		if(b[i]<now)return 0;
		if(b[i]>now){
			if(b[i]-now>1)return 0;
			if(now&&cnt<2)return 0;
			cnt = 1,now = b[i];
		}
		else cnt++;
		if(cnt>5)return 0;
	}
	if(cnt<2)return 0;
	return 1;
}
int main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	n = read();
	a[++tot].num = 1,a[tot].pos = 0;//把起止点也加进去,否则开头和末尾的零是没有填的
	for(R int i = 1;i <= n;i++){
		int x = read();
		if(x)a[++tot].num = x,a[tot].pos = i,flag[a[tot].pos]=1;
	}
	a[++tot].num = 0x3f3f3f3f,a[tot].pos = n+1;
	for(R int i = 2;i <= tot;i++){
		get_num(a[i-1].pos,a[i].pos,a[i-1].num,a[i].num);
	}	
	if(!check())puts("-1");
	else{
		printf("%d\n",b[n]);
		for(R int i = 1;i <= n;i++)printf("%d ",b[i]);
	}
	return 0;
}

posted @ 2020-10-15 15:16  HH_Halo  阅读(145)  评论(0编辑  收藏  举报