2020 CCPC Wannafly Winter Camp Day6 Div.1&2——G.单调栈【思维、贪心思想】

题目传送门


题目描述

对于一个 1... n {1...n} 1...n 的排列 p [ 1... n ] {p[1...n]} p[1...n],我们这样定义它的单调栈 f [ 1... n ] {f[1...n]} f[1...n]
对于 i {i} i,若 p [ 1... i − 1 ] {p[1...i-1]} p[1...i1] 中比 p [ i ] {p[i]} p[i] 小的数里最大的是 p [ j ] {p[j]} p[j],则 f [ i ] = f [ j ] + 1 {f[i]=f[j]+1} f[i]=f[j]+1,若不存在这样的数,则 f [ i ] = 1 {f[i]=1} f[i]=1
现在你得到了 f {f} f 中某些位置的值,求一个字典序最小的 p {p} p 使得它满足这些值。
数据保证一定有解:数据的生成方式为先随机生成一个 p {p} p,然后求出它的 f {f} f,再随机去掉一些位置的值。
对于两个数组 a [ 1... n ] {a[1...n]} a[1...n] b [ 1... n ] {b[1...n]} b[1...n],前者的字典序比后者小,当且仅当存在某个 j {j} j 使得 a [ 1... j ] = b [ 1... j ] {a[1...j]=b[1...j]} a[1...j]=b[1...j] a [ j + 1 ] < b [ j + 1 ] {a[j+1]<b[j+1]} a[j+1]<b[j+1]


输入描述:

第一行一个正整数 T {T} T 表示数据组数
对于每组数据,第一行一个正整数 n {n} n,第二行 n {n} n 个整数表示 f [ 1... n ] {f[1...n]} f[1...n],若 f [ i ] = − 1 {f[i]=-1} f[i]=1 则表示这个 f [ i ] {f[i]} f[i] 不知道是什么,你不需要去关心它。
( 1 ≤ T ≤ 100 ) (1\leq T\leq 100) (1T100) ( 1 ≤ n ≤ 100 ) (1\leq n\leq 100) (1n100)


输出描述:

对于每组数据,输出一行 n {n} n 个整数,表示字典序最小的 p [ 1... n ] {p[1...n]} p[1...n],注意不要有行末空格


输入

3
4
-1 2 -1 2
6
1 -1 2 -1 3 -1
4
-1 -1 1 -1


输出

1 3 4 2
1 3 2 5 4 6
2 3 1 4


题解

  • 首先考虑,对于所有 f [ i ] f[i] f[i]=1,说明 p [ 1... i − 1 ] p[1 ... i-1] p[1...i1] 中没有比 p [ i ] p[i] p[i] 小的,那么为了满足字典序最小, p [ i ] p[i] p[i] 应该是 1 1 1,但是可以有很多个 f [ i ] = 1 f[i]=1 f[i]=1,那么如果有 k k k f [ i ] = 1 f[i]=1 f[i]=1 ,则 p [ i . . . k ] p[i...k] p[i...k] 应该为 k . . . 1 k...1 k...1
  • 那么此时所有 f [ ] = 1 f[] = 1 f[]=1 的都确定了,可以将所有 f [ ] 减 去 1 f[]减去1 f[]1
  • 现在考虑不能确定的位置: f [ i ] = − 1 f[i]=-1 f[i]=1,题意想表达的是:对于 f [ ] = − 1 f[]=-1 f[]=1 的位置,不在上述确定 f [ ] f[] f[] 大小的考虑范围内,也就是说,对于给定的 f [ ] = − 1 f[]=-1 f[]=1,它的未知,是指由 p [ ] p[] p[] 确定 f [ ] f[] f[] 的时候,不考虑 f [ ] = − 1 f[]=-1 f[]=1 的位置。简而言之,就是 f [ ] = − 1 f[]=-1 f[]=1 的位置,后面确定 p [ ] p[] p[] 的时候,不考虑前面的 f [ ] = − 1 f[]=-1 f[]=1 的真值。
  • 搞懂题意之后,我们知道,既然 f [ ] = − 1 f[]=-1 f[]=1 的位置对 p [ ] p[] p[] 无影响,那么为了使字典序最小,在每次寻找 f [ ] = t f[]=t f[]=t的时候,第一次扫描结束之后,下一次扫描开始之前,找到第一个 f [ ] = t f[]=t f[]=t 之前的 f [ ] = − 1 f[]=-1 f[]=1 的位置,设置为 c n t + 1 cnt+1 cnt+1
  • 详情见代码
  • 这里考虑对于 f [ ] = − 1 f[]=-1 f[]=1 操作的正确性:由 样 例 2 样例2 2 推算可得,但是具体正确原因还是没搞懂,如果有明白的还请评论区讲解一下~~

AC-Code

#include <bits/stdc++.h>

using namespace std;

const int maxn = 105;

int f[maxn];
int p[maxn];

int main() {
    int T;    cin >> T;
    while (T--) {
        int n;    cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> f[i];
        }
        int t = 1;
        int cnt = 0; // 扫描的t只是为了确定顺序,而cnt则是确定位置之后填充的数字
        while (cnt < n) {
            for (int i = n; i >= 1; --i) { 
                if (f[i] == t)
                    p[i] = ++cnt;
            }
            for (int i = 1; i <= n; ++i) {
                if (f[i] == t) {
                    break;
                }
                else if (f[i] == -1) {
                    p[i] = ++cnt;
                    f[i] = t;
                    break;
                }
            }
            ++t; // 把所有数都-1浪费时间,直接令t增加,就相当于所有数都减少了1
        }
        cout << p[1];
        for (int i = 2; i <= n; ++i) {
            cout << " " << p[i];
        }
        puts("");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值