PTA (Advanced Level) 模拟题(3小时速通,熟悉PAT甲模式使用,参考自y总)

目录

1001 A+B Format

1005 Spell It Right

1006 Sign In and Sign Out

1035 Password

1036 Boys vs Girls

1050 String Subtraction

1071 Speech Patterns

1084 Broken Keyboard

1108 Finding Average

1124 Raffle for Weibo Followers

1141 PAT Ranking of Institutions

1153 Decode Registration Card of PAT

1058 A+B in Hogwarts

1136 A Delayed Palindrome

1098 Insertion or Heap Sort

1089 Insert or Merge

1018 Public Bike Management

1072 Gas Station


1001 A+B Format

题型:模拟题

思路(low):独立正负号,卡1000与100000位在卡位前用字符串获取并在前加“,”

思路(high):a + b =》 转成字符串 =》 加入“,”(注意最后一位和最后的下一位是-的情况不加“,”即可);

C++实现

#include 

using namespace std;

int main() {
    int a,b;
    cin >> a >> b;
    int c = a + b;
    string num = to_string(c);  // 转换为字符串的方法:to_string
    string res;
    for (int i = num.size() - 1, j = 0; i >= 0; i--) {
        res = num[i] + res;
        j++;
        if (j % 3 == 0 && i && num[i - 1] != '-') res = "," + res;  // 三位数 前一位是最后一位 前一位不是负号 
    }
    cout << res << endl;
    return 0;
}

1005 Spell It Right

题型:模拟题

思路:求和字符串读取,easy

#include
using namespace std;
int main() {
    string N;
    cin >> N;
    int sum = 0;
    for (auto c : N) sum += c - '0';  // 优化遍历

    string s = to_string(sum);
    char word[10][10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};   // 这里二维是因为每个字符串就是一个字符数组

    cout << word[s[0] - '0'];
    for (int i = 1; i < s.size(); i++) cout << ' ' << word[s[i] - '0'];  // 避免行位多一个空格
    return 0;
}

1006 Sign In and Sign Out

题型:模拟题

思路:字符串判断留值即可

#include
using namespace std;
int main() {
    int M;
    cin >> M;
    string minID, maxID;
    string open_time, close_time;  //  可能会想给赋个初始值,但实际情况不如用for去判断是否首项
    for (int i = 0; i < M; i++) {
        string id, in, out;
        cin >> id >> in >> out;
        
        if (!i || in < open_time) {
            open_time = in;
            minID = id;
        }
        if (!i || out > close_time) {
            close_time = out;
            maxID = id;
        }
    }
    cout << minID << " " << maxID;
    return 0;
}
/*
有唯一性:no two persons sign in or out at the same moment.
*/

1035 Password

题型:模拟题

思路:字符串读,字符串替换后比较,数组处理

#include
using namespace std;

const int N = 1001;  // 留一个预留空间(C++中要给数组不赋值的预定义就需设置为常量)
string ids[N], change_pwds[N];  // 必须要存,不然怎么把改变的密码id数打印在第一位

string change (string str) {
    string res;
    for (auto c : str) {
        if (c == '1') res += '@';
        else if (c == '0') res += '%';
        else if (c == 'l') res += 'L';
        else if (c == 'O') res += 'o';
        else res += c;
    }
    return res;
}

int main() {
    int M;
    int k = 0;
    cin >> M;
    for (int i = 0; i < M; i++) {
        string id, pwd;
        cin >> id >> pwd;
        string change_pwd = change(pwd);
        if (change_pwd != pwd) {
            ids[k] = id;
            change_pwds[k] = change_pwd;
            k++;
        }
    }
    // 表示没有困惑字符
    if (!k) {
        if (M == 1) printf("There is 1 account and no account is modified\n");
        else printf("There are %d accounts and no account is modified\n", M);  // 注意is和are,负数的acount和换行
    } else {
        cout << k << endl;
        for (int i = 0; i < k; i++) cout << ids[i] << ' ' << change_pwds[i] << endl;
    }
    return 0;
}
/*
会发现通过直接比较的实现比较不好写
可以用一个替换函数,判断它们是否相等,不相等则存入一个数组
*/

1036 Boys vs Girls

题型:模拟题

思路:判断并存值记录,依次判空存储

#include
using namespace std;
int main() {
    int N;
    cin >> N;

    string idm, namem, idf, namef;
    int minScore = 101;
    int maxScore = -1;
    
    for (int i = 0; i < N; i++) {
        string name, gd, id;
        int score;
        cin >> name >> gd >> id >> score;
        if (gd == "M" && (idm.empty() || score < minScore)) {
            minScore = score;
            idm = id;
            namem = name;
        }
        if (gd == "F" && (idf.empty() || score > maxScore)) {
            maxScore = score;
            idf = id;
            namef = name;
        }
    }

    if (idf.empty()) puts("Absent");
    else cout << namef << ' ' << idf << endl;

    if (idm.empty()) puts("Absent");
    else cout << namem << ' ' << idm << endl;

    if (!idf.empty() && !idm.empty()) cout << abs(maxScore - minScore) << endl;
    else cout << "NA" << endl;
    
    return 0;
}

1050 String Subtraction

题型:哈希表

思路:根据S2构造哈希表,再遍历S1判断哈希表即可

做法1:如果用hash表,存入后只需O(n),比较快

函数:unordered_set的insert与count方法

#include
#include
using namespace std;
int main() {
    string S1, S2, res;
    getline(cin, S1);  // 输入一个包含空格的字符串
    getline(cin, S2);

    // (1)根据S2构造哈希表
    unordered_set hash;
    for (auto c : S2) hash.insert(c);  

    // (2)遍历S1判断哈希表即可
    for (auto c : S1) 
        if (!hash.count(c)) 
            res += c;
    
    cout << res << endl;  
    return 0;
}

做法2:硬比较,不推荐(n^2比较慢)

#include
using namespace std;

string S1, S2;

bool check(char c) {
    for (auto a : S2) {
        if (a == c) return true;
    }
    return false;
}

int main() {
    string res;
    getline(cin, S1);  // 输入一个包含空格的字符串
    getline(cin, S2);
    // cin >> S1 >> S2;
    for (auto c : S1) {
        if (!check(c)) {
            res += c;
        }
    }
    cout << res << endl;  
    return 0;
}

#include 
#include 
#include 
#include 

// 基于平衡树实现:O(logn)
// 有序
set a;
map b;
multiset c;  // multi-可重复
multimap d;

// 基于hash表实现:O(1)
// 无序
unordered_set e;
unordered_map f;
unordered_multiset g;
unordered_multimap h;

1071 Speech Patterns

类型:哈希表

题目:单词拼写:选出高频单词,若不止一个,则选字典序最小的,单词连续,空格等其他符号不算

思路:
(1)map表存储单词-次数,双指针遍历,构建完map表
(2)外记录单词和长,遍历map替换,长或 同长和小(C++中second表值,first表键)

#include 
#include 
using namespace std;

bool check(char c) {  // j检查是否是需要字符
    if (c >= '0' && c <= '9') return true;
    if (c >= 'A' && c <= 'Z') return true;
    if (c >= 'a' && c <= 'z') return true;
    return false;
}

char to_lower(char c) {  // 大写转小写
    if (c >= 'A' && c <= 'Z') return c - 'A' + 'a';
    return c;
}

int main() {
    string s;
    getline(cin, s);

    unordered_map map;  // 单词 - 次数

    for (int i = 0; i < s.size(); i++) {
        if (check(s[i])) {
            string word;
            int j = i;
            while (j < s.size() && check(s[j])) word += to_lower(s[j++]);  // 双指针遍历单词
            map[word]++;
            i = j;
        }
    }

    string word;  // 记录结果单词
    int count = -1;  // 计数,记录结果长
    for (auto item : map) {
        if (item.second > count || (item.second == count && item.first < word)) {  // 长或 同长和小(C++中second表值,first表键)
            word = item.first;
            count = item.second;
        }
    }
    cout << word << " " << to_string(count);
    return 0;
}

1084 Broken Keyboard

题目:找出破损输出不了的字母,并大写 数字输出(行字符不超过80个)
思路:(哈希表)两个指针遍历,不相同则只存入字符哈希表(字母均转为大写),然后遍历时输出即可按照顺序输出即可
函数:toupper转化为大写字符,static_cast强制装换为INT类型

#include 
using namespace std;
int main() {
    string str1, str2;
    cin >> str1 >> str2;

    bool s[100] = {0};
    str2 += "?";  // 给str2加一个字符,防止越界
    for (int i = 0, j = 0; i < str1.size(); i++) {
        char a = toupper(str1[i]);
        char b = toupper(str2[j]);
        if (a == b) j++;
        else {
            if (!s[a]) 
                cout << a;
                s[a] = 1;
        }
    }

    // cout << static_cast('A') << " " << static_cast('0');   # 65 48  static_cast强制装换为Ascii
    return 0;
}

1108 Finding Average

关键在于如何判断是否是整数
思路:遍历的时候去判断是否[-1000,1000]内小数点后不超过2位的数,如何累计和和数目
    (个人想法,按字符串遍历字符去判断是否满足该数规则)
    (用try语句判断是否是正常的浮点数,然后再理性判断是否在访问和小数点是否符合)

函数:stof装换float类型,try catch语句,find找索引位,c_str把std的字符串变成c的字符串(可以给printf输出)

#include
using namespace std;
int main() {
    int count = 0;
    double sum = 0.0;

    int n;
    cin >> n;
    while (n--) {
        string num;
        cin >> num;
        double x;

        bool is_legal = true;
        try {
            // size_t idx;  // size_t是无符号整数类型,通常用于表示大小和索引。这里表示位数
            // x = stof(num, &idx);  // stof将字符串转为float类型,&idx表示变量idx的地址,记录idx字符位数
            // if (idx < num.size()) is_legal = false;  // 前面正常数字,后面乱来,比如5.20000

            x = stof(num);  // 直接此情况就可以跑过测试样例
        } catch(...) {
            is_legal = false;  // 异常情况,都是字符或多个小数点
        }

        // 判断完是否是正常浮点数后,判断是否在[-1000,1000]与小数点后不超过2位
        if (x < -1000 || x > 1000) is_legal = false;
        int k = num.find(".");  // 找不到会返回-1,是索引位
        if (k != -1 && num.size() - k > 3) is_legal = false;
        
        if (is_legal) count++, sum+=x;
        else printf("ERROR: %s is not a legal number\n", num.c_str());  // c_str()将std::string对象转换为C风格字符串(以空字符'\0'结尾的字符数组)
    }
    if (count == 0) printf("The average of 0 numbers is Undefined");
    else if (count == 1) printf("The average of 1 number is %.2f", sum);  // 这里需要靠调测测出,PAT常有这种加不加s语法操作
    else printf("The average of %d numbers is %.2f", count, sum / count);
    return 0;
}

1124 Raffle for Weibo Followers

M N S 分别代表 转发总量、中奖间隔 第一位中奖者的序号
比如样例1中
Imgonnawin!
PickMe
PickMeMeMeee
就会选PickMe(2号),然后每隔三选取,若选取重复的,则选取下一个没抽过的,再开始按间隔给奖励

实现:哈希表(Set)
思路:存储抽过奖的人入哈希表,然后判断输出即可
(1)实现1,边遍历边操作,同时i走主遍历,k走间隔遍历,符合k或s清空k,否则k++
(2)实现2,遍历和操作分开,先主遍历,然后k做主遍历,符合k+n,不符合k++

#include
#include
using namespace std;
int main() {
    int m, n, s;
    cin >> m >> n >> s;
    unordered_set set;
    if (m < s) cout << "Keep going..."; 
    else
        for (int i = 0, k = 0; i < m; i++, k++) {
            string str;
            cin >> str;
            if (i == s - 1) {  // 首次遍历
                set.insert(str);
                cout << str << endl;
                k = 0;
            } else if (i >= s && k == n) {  // 后续遍历
                if (set.count(str)) k = n - 1;  // 是否访问过
                else {
                    set.insert(str);
                    cout << str << endl;
                    k = 0;
                }
            }
        }
    return 0;
}

1141 PAT Ranking of Institutions

实现:哈希表 + 迭代器
思路:遍历存入hash(name-结构体类型),按规则赋值(因为排序是横向多维的,只能依靠k-v的其中一方)
     将value装入vector,并重写operator按规则比较,最后遍历赋值rank,输出

函数:sort,begin,end,tolower,c_str,size

#include
#include  // School - AllScore(这里用School的结构体,为了最后排序后还能把其他数据一起读出,如NS)
#include  // 最后的为了排序
#include  // 引入sort函数引入的
using namespace std;

struct School {
    string name;  // School
    double sum;  // TWS
    int count;  // NS
    School() : sum(0),count(0) {}  // 构造函数
    bool operator< (const School &t) const {
        if (sum != t.sum) return sum > t.sum;  // 分数高的靠前
        if (count != t.count) return count < t.count;  // 分数相同时,人数高的靠前
        return name < t.name;  // 最后才是名称排序前的靠前
    }
};

int main() {
    int n;
    cin >> n;
    unordered_map map; 
    while (n--) {
        string id, sch;
        double score;
        cin >> id >> score >> sch;
        // 小写化
        for(auto& c : sch) c = tolower(c);  // tolower函数只能用在字符上(相当于将sch的字母全部遍历小写)
        // 按加分规则加分
        if (id[0] == 'B') map[sch].sum += score / 1.5;
        else if (id[0] == 'A') map[sch].sum += score;
        else map[sch].sum += score * 1.5;
        // 入hash表
        map[sch].name = sch;
        map[sch].count++;
    }
    vector schs;
    for (auto item : map) {
        item.second.sum = (int) item.second.sum;  // 取整
        schs.push_back(item.second);  // 将value塞入vector,准备排序
    }
    sort(schs.begin(), schs.end());  // 排序(begin end分别代表起始迭代器和结束迭代器)
    // 输出
    cout << schs.size() << endl;
    int rank = 1;
    for (int i = 0; i < schs.size(); i++) {
        auto s = schs[i];
        if (s.sum != schs[i - 1].sum) rank = i + 1;
        printf("%d %s %d %d\n", rank, s.name.c_str(), (int) s.sum, s.count);
    }
    return 0;
}

1153 Decode Registration Card of PAT

N M 准考证数 结果需要数
遇1,扫准考证号首字母,按分数降序排,同分按字母序增序排 (意味使用哈希表,同时自建结构体,同时使用vector才能付出两维排序)
遇2,扫准考证号考场号,用一个变量累计和即可,没有对应的考场号输出NA
遇3,扫描准考证号时间,按考场号-对应当天考场号人数 输出,按人数非升序,考场号升序

hash 准考证-结构体(准考证 + 分数)
hash 考场时间-结构体(考场号 + 人数)

情况1可预知:1 A  1 B  1 T 3个hash(准考证-准考证和分数)
情况2可预知:2 考场号 1个hash(考场号-分数和)
情况3可预知:3 时间 (复杂度集中)

函数:
substr取函数位数(索引,长度)
push_back推送
sort(x.begin(), x.end())排序
c_str() 转成字符串
能用printf尽量用,cout比较耗时间

#include 
#include 
#include 
#include 
#include 
using namespace std;

struct rs {  // register score
    string reg;
    int score;
    bool operator< (const rs &r) const {  // true靠前的原则
        if (score != r.score) return score > r.score;
        return reg < r.reg;  // 成绩相同的情况下再去比较其他的
    }
};

struct tn {  // test number
    string date;
    int number;
    int score;
    tn() : number(0),score(0) {} 
};

struct gn {  // grade number
    string grade;
    int number;
    gn() : number(0) {}
    bool operator< (const gn &g) const {
        if (number != g.number) return number > g.number;
        return grade < g.grade;
    }
};

int main() {
    int n, m;
    cin >> n >> m;

    unordered_map hashA, hashB, hashT;
    unordered_set set;
    unordered_map hash2;
    
    for (int i = 0; i < n; i++) {
        string reg;
        int score;
        cin >> reg >> score;
        set.insert(reg);

        char c = reg[0];
        if (c == 'A') {
            hashA[reg].reg = reg;  // hash key结构体赋值直接赋值
            hashA[reg].score = score;
        } else if (c == 'B') {
            hashB[reg].reg = reg;
            hashB[reg].score = score;
        } else { 
            hashT[reg].reg = reg;
            hashT[reg].score = score;
        }
        string date = reg.substr(1, 3);
        hash2[date].number ++;
        hash2[date].score += score;
    }

    vector vecA, vecB, vecT;
    for (auto a : hashA) vecA.push_back(a.second);
    sort(vecA.begin(), vecA.end());
    for (auto a : hashB) vecB.push_back(a.second);
    sort(vecB.begin(), vecB.end());
    for (auto a : hashT) vecT.push_back(a.second);
    sort(vecT.begin(), vecT.end());

    for (int i = 0; i < m; i++) {
        int key;
        string str;
        cin >> key >> str;
        printf("Case %d: %d %s\n", i+1, key, str.c_str());
        if (key == 1) {
            if (str == "A") {
                if(vecA.empty()) printf("NA\n");
                for (auto a : vecA) printf("%s %d\n", a.reg.c_str(), a.score);
            } else if (str == "B") {
                if(vecB.empty()) printf("NA\n");
                for (auto a : vecB) printf("%s %d\n", a.reg.c_str(), a.score);
            } else {
                if(vecT.empty()) printf("NA\n");
                for (auto a : vecT) printf("%s %d\n", a.reg.c_str(), a.score);
            }
        } else if(key == 2) {
             if (hash2.find(str) == hash2.end())
                printf("NA\n");
             else 
                printf("%d %d\n", hash2[str].number, hash2[str].score);
        } else {
            unordered_map hash;
            bool bo = false;
            for (auto a : set) 
                if (a.substr(4, 6) == str) {
                    string key3 = a.substr(1, 3);
                    hash[key3].number ++;
                    hash[key3].grade = key3;
                    bo = true;
                }
            if (bo) {
                vector vec;
                for (auto a : hash) vec.push_back(a.second);
                sort(vec.begin(), vec.end());
                for (auto a : vec) printf("%s %d\n", a.grade.c_str(), a.number);
            } else {
                printf("NA\n");
            }
        }
        
    }
    return 0;
}

1058 A+B in Hogwarts

Galleon is an integer in [0,10^7], Sickle is an integer in [0, 17), and Knut is an integer in [0, 29)
比如 Sickle到18 那么就会进位到 Galleon

接收函数:scanf

#include 
using namespace std;
int main() {
    int a, b, c, d, e, f;
    scanf("%d.%d.%d %d.%d.%d", &a, &b, &c, &d, &e, &f);  // 接受类似 3.2.1 10.16.27
    // 相加
    a += d;
    b += e;
    c += f;
    // 进位
    b += c / 29;
    c %= 29;
    a += b / 17;
    b %= 17;
    printf("%d.%d.%d", a, b, c);
}

1136 A Delayed Palindrome

题型:模拟题

题目:数(不超过1千位,说明不能用数字接收,那么反向相加得用一些数组之类的方法),注意自身也可能为回文数
数 + 数的反序,若为回文返回,不会回文继续如此操作,直到找到回文或者操作满10次为止。

思路:用动态数组接受,反向入位(数位越小的索引越小,最好遇到这类题都这么干,方便定位进位),然后计数,到10次则出或者已经找到回文数出(读反向回文-vector计算-读和)

函数: puts to_string把数字变字符 c_str字符串输出

#include 
#include   // 动态大小的数组
using namespace std;

bool check(vector a) {  // 判断是否为回文数
    for (int i = 0; i < a.size() / 2; i++) {
        if (a[i] != a[a.size() - 1 - i]) {
            return false;
        }
    }
    return true;
}

int main() {
    string str;
    cin >> str;

    vector a;
    // 反向入库,更符合逻辑一点,比如123 对应上索引依次为 3 2 1,也能便于往后进位
    for (int i = str.size() - 1; i >= 0; i--) a.push_back(str[i] - '0');  // 2 5 1 7 9
    int count = -1; // 计数器
    while (++count < 10) {  // 这里++放前面是为了控制第十位成功的情况
        if (check(a)) break;  // 输入数为回文数也算
        
        string s1 = str;
        string s2;
        // bool bo = false;  // 相反的非0首位 // if (!bo and a[i] != '0') bo = true; // if (bo) // 考虑多了,不需要剔除0
        for (int i = 0; i < str.size(); i++) s2 += to_string(a[i]);  // 25179
        // 实施加法
        for (int i = 0; i < s2.size(); i++) {
            a[i] += s2[s2.size() - i - 1] - '0';  // 低位相加,这里的a[i]一定则定
            int j = i;
            while (a[j] >= 10) {
                a[j++] -= 10;
                if (j >= a.size()) a.push_back(1);
                else a[j] += 1;
            }
        }
        str = "";
        // 记录
        for (int i = a.size() - 1; i >= 0; i--) str += to_string(a[i]);  // 133221
        printf("%s + %s = %s\n", s1.c_str(), s2.c_str(), str.c_str());
    }
    if (count == 10) {
        puts("Not found in 10 iterations.");
    } else {
        printf("%s is a palindromic number.\n", str.c_str());
    }
    return 0;
}

1098 Insertion or Heap Sort

题意:第二行是原数组,第三行是某次排序数组后的结果,判断是哪种排序(结果唯一),然后输出下次排序的结果
考察:对插入排序和堆排序的了解程度
思路:因为结果唯一是,所以只要能判断是哪一种或不是哪一种,那么结果就迎刃而解了(这里可以采取匹配最简单的排序,插入排序匹配会简单一些)。
        施加排序,看是否有相等的一次,没有说明是另一种排序,匹配后输出下一次排序结果即可
        对插入排序有更简单的判断方法,根据插入排序的形式,扫到第k位,那么前k位一定是排好序的,后n-k位与原本相同——稳定

#include 
#include 
using namespace std;

const int N = 101;
int n;
int a[N], b[N];

void heap(int i, int len) {  // 这里本需参数a,但已经全局了,就不必要了
    int val = a[i];  // 准备替换
    for (int k = 2 * i + 1; k < len; k = 2 * k + 1) {  // 每次都往下一个叶子节点跳
        if (k + 1 < len && a[k] < a[k + 1]) k++;  // 左右节点取值最大的那个节点
        if (a[k] > val) {
            a[i] = a[k];  // 替换
            i = k;  // 记录下移索引
        } else {
            break;  // 节省时间,因为在外层我们遍历heap时,子树就已经是有序的了,后面不可能会被替换
        }
    }
    a[i] = val;
}

bool check() {  // 判断a与b是否相等 // 同理
    for (int i = 0; i < n; i++) if (a[i] != b[i]) return false;
    return true;
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];
    // 检查是否是插入排序
    int k = 1;
    while (b[k] >= b[k - 1]) k++;  // 因为不可能是已经排好序的,所以不必要做长度拦截
    bool bo = true;
    for (int i = k; i < n; i++) {
        if (a[i] != b[i]) {
            bo = false;
            break;
        }
    }
    if (bo) {  // 符合则是插入排序
        puts("Insertion Sort");
        sort(b, b + k + 1);  // C++中数组地址是首元素地址,而sort的end是要排序的最后一个位置的下一个位置,这里要排到b+k
        printf("%d", b[0]);
        for (int i = 1; i < n; i++) printf(" %d", b[i]);
    } else {
        puts("Heap Sort");
        // 接下来的做法是修改a做堆排序,找到a与b相同的顺序,然后获取下一个排序即可
        for (int i = n / 2 - 1; i >= 0; i--) heap(i, n);  // 首次构成大顶推(这个必须单独的,而且必须底到顶,避免有些子树小值在上)
        int j = n - 1;
        while (true) {
            bool bo = check();
            // 交换
            int temp = a[0];
            a[0] = a[j];
            a[j] = temp;
            heap(0, j--);
            if (bo) break;
        }
        printf("%d", a[0]);
        for (int i = 1; i < n; i++) printf(" %d", a[i]);
    }
    return 0;
}

1089 Insert or Merge

与上题同理,只是堆排序被换成归并排序

#include 
#include 
using namespace std;

const int N = 101;
int n;
int a[N], b[N];

bool check() {  // 判断a与b是否相等 // 同理
    for (int i = 0; i < n; i++) if (a[i] != b[i]) return false;
    return true;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) cin >> b[i];
    // 检查是否是插入排序
    int k = 0;
    while (b[k + 1] >= b[k]) k++;  // 因为不可能是已经排好序的,所以不必要做长度拦截,注意等于号
    bool bo = true;
    for (int i = k + 1; i < n; i++) {
        if (a[i] != b[i]) {
            bo = false;
            break;
        }
    }
    if (bo) {  // 符合则是插入排序
        puts("Insertion Sort");
        sort(b, b + k + 2);  // C++中数组地址是首元素地址,而sort的end是要排序的最后一个位置的下一个位置,这里要排到b+k
        printf("%d", b[0]);
        for (int i = 1; i < n; i++) printf(" %d", b[i]);
    } else {
        puts("Merge Sort");
        // 接下来的做法是修改a做归并排序,找到a与b相同的顺序,然后获取下一个排序即可
        int len = 1;
        while (true) {
            bool match = check();  // 在里面算,为了就是记录为true的下一次能一起出来
            len *= 2;
            for (int i = 0; i < n; i += len) sort(a + i, a + min(n, i + len));
            if (match) break;
        }
        printf("%d", a[0]);
        for (int i = 1; i < n; i++) printf(" %d", a[i]);
    }
    return 0;
}

1018 Public Bike Management

考点:最短路 + 深搜

(1)距离最短
(2)最初带去的数量越少越好
(3)最终带回的数量越少越好
思路:用dijsktra搜出最短路径后,暴力搜一遍过程带的车,里面的最小值就是答案,最小值

#include 
#include 
#include   // 才可用memset
using namespace std;

const int N = 501, INF = 0x3f3f3f3f;
int C, n, S, m;  // 站点最大容量,站点数,问题站(终点站),道路数(路径数)
int stop[N];  // 起始时每个点的停车数量
int g[N][N];  // 记录站点距离
int dist[N];  // 记录某点到某点过程的最短距离
bool visit[N];  // dijsktra的记忆数组

vector path, ans;  // 存储路径和最优值
int send = INF, bring = INF;  // 赋值正无穷

void dijkstra() {
    memset(dist, 0x3f, sizeof dist);  // 初始为正无穷
    dist[S] = 0;  // 每个点到S的距离,故必须要初始化S
    for (int i = 0; i < n; i++) {
        int j = -1;
        for (int k = 0; k <= n; k++)
            if (!visit[k] && (j == -1 || dist[k] < dist[j])) j = k;  // 最小的dist,开始时,最小的dist即为终点
        visit[j] = true;
        for (int k = 0; k <= n; k++) {
            dist[k] = min(dist[k], dist[j] + g[j][k]);
        }
    }
}

void dfs(int u, int s, int mins) {
    if (u) {  // 除了起点0(起点不需要补自行车)
        s -= (C + 1) / 2 - stop[u];  // 缺的数
        mins = min(mins, s);  // 记录最小数
    }
    if (u == S) {  // 走到终点
        int sd = abs(min(mins, 0));  // 正数为0,负数为自己
        int bg = s + sd;  // 缺的数 + 最小数 = 初始带的数
        if (sd < send) ans = path, send = sd, bring = bg;  // 查找最少带出
        else if (sd == send && bg < bring) ans = path, bring = bg;  // 查找最少带回
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (dist[u] == g[u][i] + dist[i]) {  // 如果是最短路径
            path.push_back(i);
            dfs(i, s, mins);
            path.pop_back();
        }
    }
}

int main() {
    cin >> C >> n >> S >> m;
    for (int i = 1; i <= n; i++)  cin >> stop[i];
    memset(g, 0x3f, sizeof g);  // 初始化为 正无穷
    for (int i = 0; i < m; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        g[x][y] = g[y][x] = min(g[x][y], z);
    }

    dijkstra();
    path.push_back(0);
    dfs(0, 0, 0);

    cout << send << ' ' << 0;
    for (int i = 1; i < ans.size(); i++) 
        cout << "->" << ans[i];
    cout << " " << bring << endl;
    return 0;
}

1072 Gas Station

dist <= D(都要在范围内)
最小的dist最大
dist平均数越大 等价于 总和越大
做dijkstra

#include 
#include   // 才可用memset
using namespace std;

const int N = 1020, INF = 0x3f3f3f3f;
int n, m, K, D;  // 房屋总数,加油站候选位置数, 连接房屋或加油站的道路总数 加油站的最大服务范围
int g[N][N];
int dist[N];
bool visit[N];

int get(string s) {
    if (s[0] == 'G') return n + stoi(s.substr(1));  // 如果第一个字符是 'G',则从字符串的第二个字符开始提取子字符串,并将其转换为整数。然后,返回该整数值 + n (防与房屋重复)。
    return stoi(s);  // 如果第一个字符不是 'G',则直接将整个字符串转换为整数,并返回该整数值。
}

void dijkstra(int start, int &mind, int &sumd) {
    memset(dist, 0x3f, sizeof dist);
    memset(visit,0,sizeof visit);//因为枚举,所以每次都有进行初始化 

    dist[start] = 0;
    for (int i = 0; i < n + m; i++) {
        int t = -1;
        for (int j = 1; j <= n + m; j++) 
            if (!visit[j] && (t == -1 || dist[j] < dist[t])) t = j;
        visit[t] = true;
        for (int j = 1; j <= n + m; j++) 
            dist[j] = min(dist[j], dist[t] + g[t][j]);
    }

    for (int i = 1; i <= n; i++) 
        if (dist[i] > D) {
            mind = -1;
            return;
        }
    mind = INF, sumd = 0;
    for (int i = 1; i <= n; i++) {
        mind = min(mind, dist[i]);
        sumd += dist[i];
    }
}

int main() {
    cin >> n >> m >> K >> D;
    memset(g, 0x3f, sizeof g);
    while (K--) {
        string a, b;
        int z;
        cin >> a >> b >> z;
        int x = get(a), y = get(b);
        g[x][y] = g[y][x] = min(g[x][y], z);  // 防止重复路径
    }
    int res = -1, mind = 0, sumd = INF;
    for (int i = n + 1; i <= n + m; i++) {
        int d1, d2;
        dijkstra(i, d1, d2);
        if (d1 > mind) res = i, mind = d1, sumd = d2;
        else if (d1 == mind && d2 < sumd) res = i, sumd = d2;
        
    }
    if (res == -1) puts("No Solution");
    else printf("G%d\n%.1lf %.1lf", res-n, (double)mind, (double)sumd/n+1e-8);
    return 0;
}

你可能感兴趣的:(数据结构与算法,算法,c++,数据结构)