PAT-2021年春季考试-顶级题解

前言:

非常热乎的题解,嘿嘿。以往难度不知道(没刷过他们的题库),感觉这次顶级难度真不高啊,,可能是题目难读的原因嘛?之前听cy姥姥吹的那么nb。没想到还是比较简单,唯一难读的就是题意,比较绕。

A.共享单车回收:Floyd,模拟

题目大意:

给你一张含 ( n + 1 ) (n+1) (n+1)个点(编号 0 , 1 , 2 , . . . , n 0,1,2,...,n 0,1,2,...,n) m m m条边的带边权无向图。从0点出发。每次选择尚未访问过的节点中最短路径最短的点,去往那里。以此往复。直到无点可走。

第一行输出访问节点的路径.

第二行,若这张图连通。输出总花费。反之输出未访问到的点.

n ≤ 200 n \leq 200 n200

题目思路:

直接按照题意模拟就完了。

F l o y d Floyd Floyd预处理出两点之间的最短路。然后 d f s dfs dfs模拟这个过程即可。最后判一下是否连通。很容易的题目. O ( n 3 ) O(n^3) O(n3).

B.剪草:LIS+求具体方案

题目大意:

给你 n n n个数。让你将一些数变大或者变小使得数列变成非升或者非降的。问最小更改的个数。

如果改成非升的和改成非降的都是最小更改(也就是有多解),则尽量让【变大的数】的个数最少(这个部分保证数据中答案一定唯一).

第一行输出 x x x代表删除的个数
第二行输出 x x x个数,从小到大,代表删除的下标

n ≤ 2000 n \leq 2000 n2000
1 ≤ a i ≤ 1 e 3 1 \leq a_i \leq 1e3 1ai1e3

题目思路:

1.我们只计算【变成非降】的结果,对于【变成非升】的结果,我们直接对原序列 r e v e r s e reverse reverse一下再跑非降就好了。

2.显然最小更改 = n − 最 长 非 降 子 序 列 n-最长非降子序列 n.所以我们 O ( n 2 ) O(n^2) O(n2)求一个最长非降子序列。再逆序确定dp的具体方案。

2.1:假设方案的下标序列为: b 1 , b 2 , . . . , b k , ( k 为 最 长 非 降 子 序 列 的 长 度 ) b_1,b_2,...,b_k,(k为最长非降子序列的长度) b1,b2,...,bk,(k).

我们的结论是,对于 b i b_i bi,我们一定是在左边区域找离 b i b_i bi最近的且满足 d p [ x ] = i − 1 dp[x]=i-1 dp[x]=i1 x x x当作 b i − 1 b_{i-1} bi1.

口胡:
假设有两个位置 x , y x,y x,y同时满足 d p = i − 1 dp=i-1 dp=i1,且 x < y x < y x<y.那么易知 a [ x ] > a [ y ] a[x]>a[y] a[x]>a[y]一定成立.所以如果选择 y y y,那么往后 a [ x ] a[x] a[x]都不会对答案作贡献,但是如果我们选择 x x x一定会导致 a [ y ] a[y] a[y]被计算一次贡献.

那么假设选 y y y的【变大的个数】为 c c c。那么选 x x x的【变大的个数】一定 ≥ c + 1 \geq c+1 c+1.(画个图就出来).所以选最近的最好.

3.最后对于非升非降分别处理完后再作一些讨论就好了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 2000 + 5;
const int mod = 1e9 + 7;
int a[maxn] , dp[maxn] , n;
int solve ()
{
    
    
    memset(dp , 0 , sizeof dp);
    for (int i = 1 ; i <= n ; i++){
    
    
        dp[i] = 1;
        for (int j = 1 ; j < i ; j++)
            if (a[j] <= a[i]) dp[i] = max(dp[i] , dp[j] + 1);
    }
    return *max_element(dp + 1 , dp + 1 + n);
}
vi gg;
void dfs (int id , int x , int & res)
{
    
    
    if (x == 1) {
    
    
        for (int i = 1 ; i < id ; i++){
    
    
            gg.pb(i);
        }
        return ;
    }
    int nid = n;
    for (int i = id - 1 ; i >= 1 ; i--){
    
    
        if (dp[i] == x - 1){
    
    
            nid = i;
            break;
        }
    }
    for (int i = nid + 1 ; i < id ; i++){
    
    
        if (a[i] < a[nid]) res++;
        gg.pb(i);
    }
    dfs(nid , x - 1 , res);
}
vi calc (int & res)
{
    
    
    int mx = *max_element(dp + 1 , dp + 1 + n);
    int id;
    for (int i = n ; i >= 1 ; i--){
    
    
        if (dp[i] == mx){
    
    
            id = i;
            break;
        }
    }
    gg.clear();
    res = n - id;
    for (int i = id + 1 ; i <= n ; i++){
    
    
        gg.pb(i);
    }
    dfs(id , mx , res);
    sort(gg.begin(),gg.end());
    return gg;
}
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1 ; i <= n ; i++){
    
    
        cin >> a[i];
    }
    int x = solve();
    int res1 = 0;
    vi ans1 = calc(res1);
    reverse(a + 1 , a + 1 + n);
    int y = solve();
    int res2 = 0;
    vi ans2 = calc(res2);

    vi ans;
    int len;
    if (x == n){
    
    
        cout << "Non-descending" << endl;
        return 0;
    }
    if (y == n){
    
    
        cout << "Non-ascending" << endl;
        return 0;
    }
    if (x > y){
    
    
        len = n - x;
        ans = ans1;
    }else if (x < y){
    
    
        len = n - y;
        ans = ans2;
        for (auto & g : ans) g = n - g + 1;
        sort(ans.begin(),ans.end());
    }else if (res1 < res2){
    
    
        len = n - x;
        ans = ans1;
    }else {
    
    
        len = n - y;
        ans = ans2;
        for (auto & g : ans) g = n - g + 1;
        sort(ans.begin(),ans.end());
    }

    cout << len << endl;
    int m = ans.size();
    for (int i = 0 ; i < m ; i++){
    
    
        cout << ans[i];
        if (i == m - 1) cout << endl;
        else cout << " ";
    }
    return 0;
}

C.排序+并查集:

题目大意:

给你两个序列 a , b a,b a,b.若 m i n ( a l , a l + 1 , . . . , a r ) ≥ b r − l + 1 min(a_l,a_{l+1},...,a_r) \geq b_{r-l+1} min(al,al+1,...,ar)brl+1,则称区间 [ l , r ] [l,r] [l,r]是好的.问你对于每个区间长度 k , k ∈ [ 1 , n ] k,k \in [1,n] k,k[1,n],是否存在长度为 k k k的好区间.

题目思路:

1.暴力怎么求?

对于每个确定的 b [ i ] b[i] b[i].我们将 a a a数组中所有 < b [ i ] < b[i] <b[i]的数都置为 − 1 -1 1.然后求最长连续的合法区间。令其为 c c c.比较 i , c i,c i,c的大小即可.

2.如果我们对 b b b数组降序排序,那么上面对 a a a数组的操作就是连续的.可以想象最开始 a a a是全 − 1 -1 1的.然后慢慢向里面放数。每次问最长连续区间.这个部分我们可以带权并查集维护,也可以线段树维护.

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/114753755