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 很好用 尤其是要重复操作相同操作 注意回溯的

浙公网安备 33010602011771号