Contents

2019牛客多校3 I

题目

题目链接

Median

题意

3个数产生一个中位数,现在给你一串中位数,请还原出一个合理的原串

Input

T组,每组给n表示原串的长度,然后是给你中位数串b[1]->b[n-1]

范围: n的和不超过10^6,每个b不超过10^9

Output

有合理的串则输出原串,否则输出-1

题解

结论

若存在合理的解,那么解的每个位置的最终值一定是它能影响到的3个中位数之一

证明

https://cdn.jsdelivr.net/gh/wolfdan666/BlogPic/%E7%AE%97%E6%B3%95/2019%E5%B9%B4%E5%A4%9A%E6%A0%A1/%E7%89%9B%E5%AE%A2/%E7%AC%AC%E4%B8%89%E5%9C%BA/N3I_1.png

Dp解法

https://cdn.jsdelivr.net/gh/wolfdan666/BlogPic/%E7%AE%97%E6%B3%95/2019%E5%B9%B4%E5%A4%9A%E6%A0%A1/%E7%89%9B%E5%AE%A2/%E7%AC%AC%E4%B8%89%E5%9C%BA/N3I_2.png

自己动手写AC代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include<bits/stdc++.h>
using namespace std;
const int M = 1e5+7;

bool f[M][3][3];
int pre[M][3][3];
int v[M][3];
int b[M],a[M];
int T,n;

int mid(int x,int y,int z){
    static int tmp[3];
    tmp[0]=x,tmp[1]=y,tmp[2]=z;
    sort(tmp,tmp+3);
    return tmp[1];
}

/*回溯构造*/
void back(int i,int j,int k){
    while(i>=1) {
        a[i] = v[i][j];
        int pr = pre[i][j][k];
        j = k;
        k = pr;
        i--;
    }
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=2;i<=n-1;i++) scanf("%d",&b[i]);
        /*init*/
        b[0]=b[1]=b[2]; b[n+1]=b[n]=b[n-1];
        for(int i=1;i<=n;i++)
            for(int j=0;j<3;j++)
                for(int k=0;k<3;k++)
                    f[i][j][k]=false;
        /*注意:我给的手写题解中a[3]对应b[1],b[2],b[3]
         我这里为了实现方便是用的对应b[2],b[3],b[4]*/
        for(int i=1;i<=n;i++){
            for(int j=0;j<3;j++){
                v[i][j]=b[i-1+j];
            }
            sort(v[i],v[i]+3);
        }
        /*边界条件: 前i-2个中位数(此时i=2为0个中位数)是满足条件的
          最终f[N][i][j]的时候的是N-2个中位数是否满足条件*/
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                f[2][i][j]=true;

        /*solve*/
        bool findans = false;
        for(int i=3;i<=n;i++){
            for(int j=0;j<3;j++){
                for(int k=0;k<3;k++){
                    for(int l=0;l<3;l++){
                        if(!f[i-1][k][l]) continue;
                        /*判断前面的位置和本位置使用与他们位置相关的
                        3个中位数的排列中哪些排列能够满足要求
                        v[i][j]对应的是b[i-1],b[i],b[i+1]中的一个*/
                        if(mid(v[i-2][l],v[i-1][k],v[i][j])!=b[i-1])
                            continue;
                        f[i][j][k]=true;
                        /*记录下前面使用的是l大的*/
                        pre[i][j][k]=l;
                        /*break写完再探索-std中用了break
                        我认为应该遍历全部情况,所以去掉了break
                        然后两份代码都AC了,所以可能解唯一或者是按照std
                        生成的数据吧*/
                        // break;
                    }
                    if(i==n && f[i][j][k]){
                        findans = true;
                        back(i,j,k);
                        goto END;
                    }
                }
            }
        }
        END:
        if(!findans)
            printf("%d\n",-1);
        else{
            for(int i=2;i<n;i++) {
                assert(mid(a[i-1],a[i],a[i+1]) == b[i]);
            }
            for(int i=1;i<=n;i++){
                printf("%d%c",a[i]," \n"[i==n]);
            }
        }

    }

    return 0;
}