2021 - cccc总决赛题解

L1.1  7-1 人与神 (5分)

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int  n, m;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout << "To iterate is human, to recurse divine." << "\n";
    return 0;
}

 

 

L1.2 7-2 两小时学完C语言 (5 分)

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int  n, k, m;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cin >> n >> k >> m;
    cout << n - k * m << "\n";
    return 0;
}

 

 

L1.3 7-3 强迫症 (10 分)

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int  n, k, m;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cin >> n;
    if(n > 100000){
        int year = n / 100;
        int month = n % 100;
        printf("%d-%02d\n", year, month);
    }
    else{
        int year = n / 100;
        int month = n % 100;
        if(year < 22) year = 2000 + year;
        else year = 1900 + year;
        printf("%d-%02d\n", year, month);
    }
    return 0;
}

 

 

L1.4 7-4 降价提醒机器人 (10 分)

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int  n, m;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        double price;
        cin >> price;
        if(price < m) printf("On Sale! %.1f\n", price);
    }
    return 0;
}

 

 

L1.5 7-5 大笨钟的心情 (15 分)

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int  n, k, m, a[24];

int main()
{
    for(int i = 0; i < 24; i++){
        scanf("%d", &a[i]);
    }
    int q;
    while(scanf("%d", &q), q <= 23 && q >= 0){
        if(a[q] > 50) printf("%d Yes\n", a[q]);
        else printf("%d No\n", a[q]);
    }
    return 0;
}

 

 

L1.6 7-6 吉老师的回归 (15 分)

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int  n, x;
string ss;

int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    scanf("%d %d", &n, &x);
    getchar();//也可以用getline(cin, s); 消除缓冲区的换行符
    int flag = 0;
    for(int i = 1; i <= n; i++){
        string s;
        getline(cin, s);
//也可以简便写法 s.find("xxx") != s.npos;
if(s.find("qiandao") != string::npos || s.find("easy") != string::npos){ if(i != n) continue; } else x--;//可以把x--放在下面if的后面 这样就不用再用flag 了 if(x == -1 && flag == 0) { flag = 1; ss = s; } if(i == n && x >= 0) cout << "Wo AK le\n";//可以将此行放到外面 然后前面的if(i != n )就可以省去了 } if(flag) cout << ss << "\n"; return 0; }

我上面写的可能复杂化 后半段可以更简洁点 如下

for (int i = 1; i <= n; i++) {
        getline(cin, s);
        if (s.find("easy") != s.npos || s.find("qiandao") != s.npos) continue;
        if (!x) {
            cout << s << "\n";
        }
        x--;
    }
    if (x >= 0) {
        cout << "Wo AK le" << "\n";
    }

简化段借鉴:  https://blog.csdn.net/SsteelXD/article/details/116210115

 

 

L2.1 7-7 天梯赛的善良 (20 分)

给一数组求最大值和最小值出现的次数

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int n, a[20010], g[1000010];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    int flag = 0;
    int maxn = 0, minn = inf;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        g[a[i]]++;
        if(a[i] > maxn) maxn = a[i];
        if(a[i] < minn) minn = a[i];
    }
    cout << minn << " " << g[minn] << "\n";
    cout << maxn << " " << g[maxn] << "\n";
    return 0;
}

 

L2.2 7-8 乘法口诀数列 (20 分)

先给两个一位数为数组前两个数 后面的数是前面的数的乘积 若得到的数是多位的 则每位数字都加到数组的尾部 然后依次往右移因数 (即第二次就是a[2] * a[3]) 求最后数组前k个数

 

开一个数组存答案 再开个指针指向当前因数的位置 模拟下去就好了

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int x, y, a[20010], b[10000], p, n;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> x >> y >> n;
    int cnt = 2;
    a[1] = x, a[2] = y;
    p = 2;
    while (cnt < n){
        int num = a[p] * a[p - 1];
        int tot = 0;
        if(num == 0){
            a[++cnt] = 0;
        }
        else{
            while(num > 0){
                b[++tot] = num % 10;
                num /= 10;
            }
            for(int i = tot; i >= 1; i--){
                a[++cnt] = b[i];
            }
        }
        
        p++;
    }
    for(int i = 1; i <= n; i++){
        cout << a[i] << " \n"[i == n];
    }
    return 0;
}

 

 

L 2.3 7-9 包装机 (25 分)

题意:

n个轨道 一个筐(最大容量s) 一条流水线 每次可以选择一个操作 即0 将筐中最上面的物品放到流水线上 1-n把轨道最前面的物品投入框中 给出一系列操作 以-1结束 (要注意如果筐为空或轨道为空 在做相应的操作相当于没做 而当筐满时 还要放入物品时 要先抓取一个放到流水线上然后再投入筐中)

思路:

栈应用 队列应用

 

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int m, n, s, tot, num;
char sta[105], op[1000000];
queue<char> q[105];//对应n条轨道

int main()
{
    scanf("%d %d %d", &n, &m, &s);
    getchar();//用cin时可以用getline(cin, s);来消除缓冲区的换行符
    for(int i = 1; i <= n; i++){
        char ss[1005];
        scanf("%s",ss);
        //int len = strlen(ss);
        for(int j = 0; j < m; j++){
            q[i].push(ss[j]);
        }
    }
    int k;
    while(cin >> k, k != -1){
        if(k){//不为0
            if(tot == s) {//如果筐满了 
                if(!q[k].empty()){//注意筐满了也要判断轨道上还有没有物品如果没有也就不用拿出了
                    op[++num] = sta[tot]; //这点容易忽略 我就是忽略了这一点导致最后一个测试点没过
                    tot --;
                    sta[++tot] = q[k].front();
                    q[k].pop();
                }
                
            }
            else {
                if(!q[k].empty()){//筐没满判断一下是否还有东西要放入
                    sta[++tot] = q[k].front();
                    q[k].pop();
                }
            }
            
        }
        else {
            if(tot > 0){//筐不为空
                op[++num] = sta[tot];
                tot --;
            }
            
        }
    }
    for(int i = 1; i <= num; i++){
        printf("%c", op[i]);
    }
    printf("\n");
    return 0;
}

 

 

L2.4 7-10 病毒溯源 (25 分)

题意:

一个病毒源可以变异成多个病毒 且每个病毒都只由一个病毒变异而来也不存在循环变异 求最长的变异链 若存在多个答案按字典序小的输出

思路:

先每个点的儿子排序 (这样保证输出可以是字典序最小)然后 广搜(bfs)记录每个点的父亲 每次更新最长链长 和尾节点序号 bfs完之后 从尾结点开始 将这条链的序号都装入vector中倒序输出即可

之前一直有几个测试点没过 以为是一定要用dfs 后来想想其实不然 bfs也可以的 而错误在于我没有再bfs的过程中同步更新最长链长和尾结点 而是再bfs完后再遍历一遍 再最长链长 这样就不准了 不是字典序最小了

例如 :1 3 2 和 1 2 4 错误的方法会输出1 3 2它是按最后一个序号比大小的

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int maxx, mm;
int m, n, first, tot, num, f[10005], fa[10005], len[10005];
vector<int>g[10005];
queue<int>q;

//因为题目明确说明一个点最多只有一个父亲 所以不用加vis[ ]每个点最多入队1次
void bfs(int x){
    maxx = 1, mm = x;
    len[x] = 1;
    q.push(x);
    while(!q.empty()){
        int now = q.front();
        q.pop();
        for(int i = 0; i < g[now].size(); i++){
            fa[g[now][i]] = now;
            q.push(g[now][i]);
            len[g[now][i]] = len[now] + 1;
            if(len[g[now][i]] > maxx){//同步更新最长链信息
                maxx = len[g[now][i]];
                mm = g[now][i];
            }
        }
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        int m;
        scanf("%d", &m);
        while(m --){
            int x;
            scanf("%d", &x);
            f[x] = 1;//标记为某个点的儿子
            g[i].push_back(x);
        }
        sort(g[i].begin(), g[i].end());//儿子排序
    }
    
    for(int i = 0; i < n; i++){
        if(!f[i]){
            first = i;
            bfs(i);
            break;
        }
    }
    
    int now = mm;
    vector<int> l;
    while(f[now]){
        l.push_back(now);//最长链装入容器中
        now = fa[now];
    }
    printf("%d\n", maxx);
    printf("%d", first);//源头
    for(int i = l.size() - 1; i >= 0; i--){//倒序输出
        if(i == 0) printf(" %d\n", l[i]);//注意输出格式问题
        else printf(" %d", l[i]);
    }
    return 0;
}
这题的注重点就是bfs或dfs 和 儿子排序 、同步更新答案值 达到字典序的效果

 

L2.5 7-11 清点代码库 (25 分)

题意:

给出n个含m个数的数组 求有几个不同的数组 并按重复次数大到小输出这几个数组 (如果有并列按字典序的顺序输出)一行开头输出数组重复个数

思路:

原来vector 是可以排序的 且装入map或set或vector中 知识浅薄的我又涨知识了 

这道题就是考stl应用 

将同一个数组的元素装入一个vector容器中 用map记入相同vector容器的出现次数 并实现 vector 排序

再开一个vector<vector<int> >v[N]其中N为重复次数 这就把重复次数相同的vector归为一类按字典序装到了一个更大的容器中v[i] 最后按v的关键字(即重复次数)大到小输出 即可

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int m, n;
map<vector<int>, int> mp; //map中以vector 为关键字可以自动对vector按字典序排序
vector<vector<int> > v[10005]; 
vector<int> ve;

int main()
{
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++){
        ve.clear();//注意每次清除
        for(int j = 1; j <= m; j++){
            int now;
            scanf("%d", &now);
            ve.push_back(now);
        }
        mp[ve]++;//储存数据
    }
    cout << mp.size() << "\n";
    for(auto x : mp){//遍历mp的关键字 这样写简便尤其是遍历容器中的元素
        v[x.second].push_back(x.first);//以重复次数相同的vector放入同一个容器中且已实现字典序
    }
    for(int i = n; i >= 1; i--){//倒序输出从n开始 即 按重复次数大到小
        for(auto j : v[i]){
            printf("%d", i);
            for(auto k : j){
                printf(" %d", k);
            }
            printf("\n");
        }
    }
    return 0;
}

 

L2.6 7-12 哲哲打游戏 (25 分)

题意:

给出n个情节点信息 表示当前情节点可以去往的情节点 然后再给出m个操作 0 j 表示去往当前情节点指向的第j个情节点 1 j 表示把当前情节点存档到第j个档位中 2 j 表示去往第j个档位存的情节点中 要求顺序输出存过的情节点 并最后输出最后到达的情节点序号

思路:

就简单模拟就好了 先存图把几个情节点的关系的信息存下来 用领接表 再开一个数组存档位中的情节点 从头开始模拟一遍就好了

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int m, n, s, tot, num, res[N];
vector<int> g[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        int cnt;
        cin >> cnt;
        while(cnt --){
            int x;
            cin >> x; //存图
            g[i].push_back(x);
        }
    }
    int op, pos, now = 1;
    for(int i = 1; i <= m; i++){
        cin >> op >> pos;
        if(op == 1){
            res[pos] = now;
            cout << now << "\n";
        }
        else if(op == 0){
            now = g[now][pos - 1];
        }
        else if(op == 2){
            now = res[pos];
        }
    }
    cout << now << "\n";
    return 0;
}

 

 

L3.2 7-14 还原文件

题意:

给出半张纸n个点的高度 然后再给出m个纸片的k个折点的高度求纸片恰好和半张纸拼起来的序列

思路:

对于每张纸片用vector<int > v[N]容器来储存它缺口的折点点 v[i]代表序号为i的纸片的折点集 再用另一个vector<int> h[i]容器来 储存第一个折点高度为i的纸片的序号集 然后用dfs 深搜 寻找答案 

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f; 
int m, n, dots[N], ans[N], vis[110], tot;//dots储存半张纸的折点高度
vector<int> part[110], fnum[110];//part储存纸片上的折点高度 fnum储存第一个折点高度为i的纸片序号

bool check(int now, int pos){//判断纸片缺口能否对应上
    for(int i = 0; i < part[now].size(); i++){
        if(part[now][i] != dots[pos]) return false;//细节 注意dots 
        pos++;
    }
    return true;
}

void dfs(int pos, int cnt){
    if(pos >= n) return;//已经找到答案
    for(int i = 0; i < fnum[dots[pos]].size(); i++){
        int now = fnum[dots[pos]][i];//注意关系 
        if(vis[now]) continue;//一个纸片最多用一次
        if(check(now, pos)){
            vis[now] = 1;
            ans[cnt] = now;
            dfs(pos + part[now].size() - 1, cnt + 1);//注意pos 再dfs里面更新 在外面更新会影响后续操作
            vis[now] = 0;//注意回溯 可能前面没有找到答案就要重新往另一个方向找
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> dots[i];
    }
    cin >> m;
    for(int i = 1; i <= m; i++){
        int cnt;
        cin >> cnt;
        while(cnt --){
            int x;
            cin >> x;
            part[i].push_back(x);//每个纸片的折点装到一个容器中 归一类
        }
        fnum[part[i][0]].push_back(i);//第一个高度为x的vector装所有符合条件的序号 
    }
    dfs(1, 1);
    for(int i = 1; i <= m; i++){
        cout << ans[i] << " \n"[i == m];
    }
    return 0;
}

这题用到了dfs每次都是在剩余的纸片中找符合的 所以可以递归实现 用vis标记 因为 不同纸片起始点高度可能一样(列如 半张纸缺口为 1 2 3 1 2 两个纸片为 1 2 和 1 2 3,就可能1 2 1 2 3 不行 然后返回去 ) 如果简单模拟到达最后发现不行 还要返回 太过复杂 而如果用dfs就可以很好地解决这个问题 因为它可以回溯  这题也用到了vector将一类的装入同一个容器

 

刷了cccc - 21的收获

1.string类s.find()函数的用法 s.find(" ") != s.npos 用来查找子串

2.要实现链的字典序 可以将 他们的儿子先排序再 深搜或广搜

3.最大最小最长的题目要有时用好同步更新

4.vector之间也可以比大小 要用好stl map和set都可以实现键值自排 vector<int>v[N]也可以只是不是连续的

5.好好利用vector容器可以用于归类

6.dfs 很好用 尤其是要重复操作相同操作 注意回溯的 

 

posted @ 2022-02-28 14:28  Yaqu  阅读(297)  评论(0)    收藏  举报