首页 PAT-Recover the Smallest Number & Perfect Sequence & Insert or Merge & 二分查找函数upper_bound()
文章
取消

PAT-Recover the Smallest Number & Perfect Sequence & Insert or Merge & 二分查找函数upper_bound()

A1038 Recover the Smallest Number

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
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;

bool comp(string& s1, string& s2)
{
    /*
    int len = s1.size() > s2.size() ? s1.size() : s2.size();//取长度最长的来比较
    //短的string到头了就要从头接着比
    for (int j = 0; j < len; j++)
    {
        if (s1[j % s1.size()] == s2[j % s2.size()])//如果相同位相同
        {
            continue;
        }
        else//如果不同,小的在前面
        {
            return s1[j % s1.size()] < s2[j % s2.size()];
        }
    }
    //如果遍历到最后都相等,其实怎么排都无所谓了
    return true;*/
    return s1 + s2 < s2 + s1;//如果s1 + s2小,就把s1排前面
}

int main()
{
    int N;
    cin >> N;//数字片段个数
    vector<string> segs;
    for (int i = 0; i < N; i++)
    {
        string _num;
        cin >> _num;
        segs.emplace_back(_num);
    }
    //排序
    sort(segs.begin(), segs.end(), comp);
    //输出:记得去掉先导0
    bool zero;
    if (segs[0][0] == '0')zero = true;//说明有先导0
    else zero = false;
    for (int i = 0; i < segs.size(); i++)
    {
        if (zero)//如果当前还有先导0
        {
            for (int j = 0; j < segs[i].size(); j++)//遍历当前字符串的每个字符
            {
                if (segs[i][j] != '0')//如果已经不是0了,那就把剩下的输出
                {
                    zero = false;
                    cout << segs[i].substr(j);
                    break;
                }
            }
        }
        else//当前已经没有先导0
        {
            cout << segs[i];
        }
    }
    if (zero)//如果到最后了都是0
    {
        cout << 0;
    }
}

    这道题按我的程序做完后,有两个测试点(一共6分)过不去,其中一个是特判,如果号码全是0,那么要输出0,还有一处,其实是我comp函数写的不对(注释里的是我写的),我已经想到了两个长度不同的字符串顺序不同大小不同这一点,但我进行判断的方法很复杂,还不能保证一定对,因为我是按长串的长度来一一比对,短串到头了再重新比,如果都相同就return false(此时我觉得,return什么都行),但实际上不应该是这样,比如123和1231,按我的方法,这两个串就是相等了,谁放在前面都行,但1231123是小于1231231的,像我写的return false的话,123会放在1321的前面,这样答案就不对了,改成true可以通过这一个样例,但实际上还是不对的,如果样例多一点,肯定还是不行的。

    直接两串相加对比,既简单,又正确 。

A1085 Perfect Sequence

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
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef  long long LL;
int main()
{
    LL N, p;
    cin >> N >> p;//序列中元素个数N,参数p
    vector<LL> sequence;
    //读取每个数
    for(int i = 0; i < N; i++)
    {
        LL _num;
        cin >> _num;
        sequence.emplace_back(_num);
    }
    sort(sequence.begin(), sequence.end());
    LL goal; 
    int ans = 0;
    //从最小的开始遍历,二分查找右侧第一个大于m*p并返回它的位置
    for (int i = 0; i < N; i++)
    {
        goal = sequence[i] * p;
        //pos为第一个大于m*p的位置,pos-i处的位置正好是等于序列长
        auto pos = upper_bound(sequence.begin(), sequence.end(), goal);
        int len = pos - (sequence.begin() + i);
        if (len > ans)
        {
            ans = len;
        }
    }
    cout << ans;
}

    这道题目其实从某种意义上来讲是暴力算法,只是用二分把复杂度从$O(n^2)$降到了$O(nlog(n))$,其中upper_bound()函数就是二分查找,非常方便,记录下

upper_bound() & lower_bound()

  • upper_bound(I first, S last, const T& value)

指向首个大于 value 的元素的迭代器,或若找不到这种元素则为 last

  • lower_bound( ForwardIt first, ForwardIt last, const T& value );

指向首个不小于 value 的元素的迭代器,或若找不到这种元素则为 last

A1089 Insert or Merge

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
99
100
101
102
103
104
105
106
107
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;


bool isSame(vector<int>& tmp, vector<int>& partial)
{
    for (int i = 0; i < tmp.size(); i++)
    {
        if (tmp[i] != partial[i]) return false;
    }
    return true;
}
bool insertSort(vector<int>& origin, vector<int>& partial)
{
    bool flag = false;//标记是否和部分排序的数组一样了
    for (int i = 1; i < origin.size(); i++)//进行size()-1次排序
    {
        //先判断是否和部分排序的数组一样了
        if (i != 1 && isSame(origin, partial))//初始数组不进行判断
        {
            flag = true;
        }
        //插入排序
        int tmp = origin[i];//储存当前要插入的元素
        int j = i;
        while (j > 0 && origin[j - 1] > tmp)//如果前面一个元素大于要插入的元素
        {
            origin[j] = origin[j - 1];
            j--;
        }
        origin[j] = tmp;
        //判断是否可以退出了
        if (flag)//此时说明是插入排序,而且下一步也做好了
        {
            return true;
        }
    }
    return false;//排完了还不相等
}

void mergeSort(vector<int>& origin, vector<int>& partial)
{
    bool flag = false;
    //以2为步长,每次翻倍
    for (int step = 2; step / 2 < origin.size(); step *= 2)
    {
        //先判断是否和部分排序数组一致
        if (isSame(origin, partial))
        {
            flag = true;
        }
        //归并排序
        for (int i = 0; i < origin.size(); i += step)
        {
            sort(origin.begin() + i, min(origin.begin() + i + step, origin.end()));
        }
        //如果一致了
        if (flag)
        {
            return;
        }
    }
}

void showArray(vector<int> &array)
{
    for (int i = 0; i < array.size(); i++)
    {
        if (i != 0)
        {
            cout << ' ' << array[i];
        }
        else
        {
            cout << array[i];
        }
    }
}
int main()
{
    int N;//序列数字个数
    cin >> N;
    vector<int> origin(N);
    vector<int> partial(N);
    for (int i = 0; i < N; i++)
    {
        cin >> origin[i];
    }
    for (int i = 0; i < N; i++)
    {
        cin >> partial[i];
    }
    vector<int> tmp(origin);
    if (insertSort(tmp, partial))
    {
        cout << "Insertion Sort" << endl;
        showArray(tmp);
    }
    else
    {
        mergeSort(origin, partial);
        cout << "Merge Sort" << endl;
        showArray(origin);
    }
}

    这道题对我来说稍有难度,涉及到插入排序和归并排序的具体书写,其中归并排序的迭代写法通过每次迭代step的方式来循环。插入排序中,插入时,从数组排好序的尾部开始比对,直到比对到0或者小于要插入元素时才停止。

本文由作者按照 CC BY 4.0 进行授权