简单的填数「贪心」
题目描述
题库后缀为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;
}