第30次CCF计算机软件能力认证
100+100+100+80+100=480
重复局面
题目大意
依次给定个国际象棋局面,依次回答每个局面是第几次出现。
解题思路
拿map
记录下每个局面,统计计数即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n; cin >> n; map<string, int> qwq; while(n--){ string s; for(int i = 0; i < 8; ++ i){ string a; cin >> a; s += a; } qwq[s] ++; cout << qwq[s] << '\n'; } return 0; }
矩阵运算
题目大意
给定的矩阵 ,一个 维向量 ,计算 的结果。
解题思路
的结果是 的矩阵,因为计算结果可能会超出 范围, 的 内存有 ,因此不能按此计算。
注意矩阵乘法具有结合律,而且点乘 不会影响此处的结果,因此我们可以改为算 ,这样只有 的大小,完全可以储存和计算。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n, d; cin >> n >> d; vector<vector<LL>> q(n, vector<LL>(d, 0)), k(n, vector<LL>(d, 0)), v(n, vector<LL>(d, 0)); vector<LL> w(n, 0); for(auto &i : q) for(auto &j : i) cin >> j; for(auto &i : k) for(auto &j : i) cin >> j; for(auto &i : v) for(auto &j : i) cin >> j; for(auto &i : w) cin >> i; vector<vector<LL>> tmp(d, vector<LL>(d, 0)), tmp2(n, vector<LL>(d, 0)); for(int i = 0; i < n; ++ i) for(int j = 0; j < d; ++ j) for(int z = 0; z < d; ++ z){ tmp[j][z] += k[i][j] * v[i][z]; } for(int i = 0; i < d; ++ i) for(int j = 0; j < n; ++ j) for(int z = 0; z < d; ++ z){ tmp2[j][z] += q[j][i] * tmp[i][z]; } for(int i = 0; i < n; ++ i) for(int j = 0; j < d; ++ j) tmp2[i][j] *= w[i]; for(int i = 0; i < n; ++ i) for(int j = 0; j < d; ++ j) cout << tmp2[i][j] << " \n"[j == d - 1]; return 0; }
解压缩
题目大意
给定了一个压缩后的数据,以及解压的规则,输出解压后的数据。
解题思路
就按照规则模拟即可,注意大端模式和小端模式的数据解析。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; #define LOWER 3 #define CONST_VAL 0 #define BACK_SHORT 1 #define BACK_LONG 2 LL get_num(char c){ if (isdigit(c)) return c - '0'; else return c - 'a' + 10; } LL get_sz(const string& s, int& pos, int len){ LL sum = 0; for(int i = pos; i < pos + len; ++ i){ sum = sum * 16 + get_num(s[i]); } pos += len; return sum; } LL get_sz_small(const string& s, int& pos, int len){ LL sum = 0; LL ji = 1; for(int i = pos; i < pos + len;){ sum = sum + ji * get_sz(s, i, 2); ji <<= 8; } pos += len; return sum; } string get_string(const string& s, int& pos, int len){ string tmp = s.substr(pos, len); pos += len; return tmp; } bool up_zero(LL num){ return !((num >> 7) & 1); } LL remove_up(LL num){ if (up_zero(num)) return num; else return num ^ (1 << 7); } int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n; cin >> n; n = (n + 7) / 8; string s; for(int i = 0; i < n; ++ i){ string a; cin >> a; s += a; } int id = 0; LL sz = 0; LL ji = 1; while(true){ int sign = get_sz(s, id, 2); sz += ji * remove_up(sign); ji *= 128; if (up_zero(sign)){ break; } } string ans; while(ans.size() < sz * 2){ int sign = get_sz(s, id, 2); if ((sign & LOWER) == CONST_VAL){ int len = (sign >> 2); if (len < 60){ ++ len; ans += get_string(s, id, len * 2); }else{ len = get_sz_small(s, id, (len - 60 + 1) * 2); ++ len; ans += get_string(s, id, len * 2); } }else if ((sign & LOWER) == BACK_SHORT){ int len = ((sign >> 2) & 7) + 4; int o = ((sign >> 5) << 8) + get_sz(s, id, 2); len *= 2; o *= 2; if (o >= len){ ans += ans.substr(ans.size() - o, len); }else{ int pos = ans.size() - o; string tmp; while(len){ tmp += ans[pos]; pos ++; if (pos == ans.size()) pos -= o; -- len; } ans += tmp; } }else if ((sign & LOWER) == BACK_LONG){ int len = (sign >> 2) + 1; int o = get_sz_small(s, id, 4); len *= 2; o *= 2; if (o >= len){ ans += ans.substr(ans.size() - o, len); }else{ int pos = ans.size() - o; string tmp; while(len){ tmp += ans[pos]; pos ++; if (pos == ans.size()) pos -= o; -- len; } ans += tmp; } } } for(int i = 0; i < ans.size(); ++ i){ if (i && i % 16 == 0) cout << '\n'; cout << ans[i]; } return 0; }
电力网络
题目大意
给定一张个点 条边的无向连通图,要求给每个点规定一个颜色,共有种颜色可选,使得总代价最小。
总代价包括点的代价和边的代价,点的代价即选定该点的颜色对应的代价。边的代价由每条边两点颜色决定,以矩阵形式给出。
五个子任务:
- 点数,颜色数
- 点数 ,颜色数 ,一棵树
- 点数 ,颜色数 ,一棵基环树
- 点数 ,颜色数 ,一个图,但去掉其中的某个点及其连边,变成一棵树,且与点相连的点都是以某个点 为根的叶子
- 点数 ,颜色数 ,一个图,但度数的点数
解题思路
第一个子任务,直接暴力,枚举每个点的颜色然后求代价的最小值,复杂度为
第二个子任务,设表示 点 的颜色为的最小代价,枚举儿子的颜色,树形转移。复杂度为
第三个子任务,找到环上的任意一条边,枚举 的颜色(消除后效性),然后以 为根( 也行)做上述树形 即可。复杂度为,如何找到环上一条边,跑一下并查集即可。
第四个子任务,找到点,然后枚举点的颜色(同样消除后效性),然后以不与点有连边的点做上述树形 即可。复杂度为,如何找到点,首先统计每个点的度数,点的度数满足 (数据可能水了,有这个条件就够了,感觉单满足这个条件还不够,可能会存在把 点去掉后不联通),然后与点 相连的点的度数都为 (为叶子)
第五个子任务,如果全部度数,那么随便枚举一个点的颜色,然后上述树形 即可。否则找到度数的点,剩下的都是度数为的点,它们仅可能形成一条链(不可能是环),然后枚举每条链,再花 枚举每条链的一端颜色,做一遍上述树形 ,然后再枚举每个度数 的颜色,求最小值。复杂度为 ,但感觉不太好写,也没时间写了(
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const LL inf = 1e18; int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n, k, m; cin >> n >> m >> k; vector<vector<int>> pcost(n, vector<int>(k)); for(auto &i : pcost) for(auto &j : i) cin >> j; vector<vector<array<int, 2>>> edge(n); vector<array<int, 2>> edges(m); vector<vector<int>> ecost(m, vector<int>(k * k)); for(int i = 0; i < m; ++ i){ int u, v; cin >> u >> v; edges[i] = {u, v}; edge[u].push_back({v, i}); edge[v].push_back({u, i}); for(auto &j : ecost[i]) cin >> j; } if (n <= 6 && k <= 10){ vector<int> used(n); LL ans = inf; LL tmp = 0; function<void(int)> dfs = [&](int x){ if (x == n){ LL back = tmp; for(int i = 0; i < m; ++ i){ int u = edges[i][0], v = edges[i][1], id = i; int r = used[u] * k + used[v]; tmp += ecost[id][r]; } ans = min(ans, tmp); tmp = back; return; } for(int i = 0; i < k; ++ i){ tmp += pcost[x][i]; used[x] = i; dfs(x + 1); tmp -= pcost[x][i]; } }; dfs(0); cout << ans << '\n'; }else if (m == n - 1){ vector<vector<LL>> dp(n, vector<LL>(k, 0)); function<void(int, int)> dfs = [&](int u, int fa){ for(auto e : edge[u]){ int v = e[0], id = e[1]; if (v == fa) continue; dfs(v, u); for(int i = 0; i < k; ++ i){ LL tmp = inf; for(int j = 0; j < k; ++ j){ int L = i, R = j; if (u != edges[id][0]) swap(L, R); int r = L * k + R; tmp = min(tmp, dp[v][j] + pcost[v][j] + ecost[id][r]); } dp[u][i] += tmp; } } }; dfs(0, 0); LL ans = inf; for(int i = 0; i < k; ++ i) ans = min(ans, dp[0][i] + pcost[0][i]); cout << ans << '\n'; }else if (m == n){ vector<int> id(n); iota(id.begin(), id.end(), 0); int ignore = 0; function<int(int)> findfa = [&](int x){ return x == id[x] ? x : id[x] = findfa(id[x]); }; for(int i = 0; i < m; ++ i){ int u = edges[i][0], v = edges[i][1]; int fu = findfa(u), fv = findfa(v); if (fu == fv){ ignore = i; break; } id[fu] = fv; } vector<vector<LL>> dp(n, vector<LL>(k, 0)); LL ans = inf; int fixed = edges[ignore][0], st = edges[ignore][1]; for(int col = 0; col < k; ++ col){ for(auto &i : dp) fill(i.begin(), i.end(), 0); function<void(int, int)> dfs = [&](int u, int fa){ for(auto e : edge[u]){ int v = e[0], id = e[1]; if (v == fa || id == ignore) continue; dfs(v, u); for(int i = 0; i < k; ++ i){ if (u == fixed && i != col) continue; LL tmp = inf; for(int j = 0; j < k; ++ j){ if (v == fixed && j != col) continue; int L = i, R = j; if (u != edges[id][0]) swap(L, R); int r = L * k + R; tmp = min(tmp, dp[v][j] + pcost[v][j] + ecost[id][r]); } dp[u][i] += tmp; } } }; dfs(st, st); for(int i = 0; i < k; ++ i){ int L = i, R = col; if (st != edges[ignore][0]) swap(L, R); int r = L * k + R; ans = min(ans, dp[st][i] + pcost[st][i] + ecost[ignore][r]); } } cout << ans << '\n'; }else{ vector<int> du(n); for(int i = 0; i < m; ++ i){ int u = edges[i][0], v = edges[i][1]; ++ du[u]; ++ du[v]; } int target = 0; for(int i = 0; i < n; ++ i){ if (m - du[i] == n - 2){ target = i; break; } } vector<int> forbid(n, 0); for(auto &e : edge[target]){ int v = e[0]; forbid[v] = 1; } int st = 0; while(st == target || forbid[st]) ++ st; vector<vector<LL>> dp(n, vector<LL>(k, 0)); LL ans = inf; for(int col = 0; col < k; ++ col){ for(auto &i : dp) fill(i.begin(), i.end(), 0); function<void(int, int)> dfs = [&](int u, int fa){ for(auto e : edge[u]){ int v = e[0], id = e[1]; if (v == fa) continue; if (v != target) dfs(v, u); for(int i = 0; i < k; ++ i){ LL tmp = inf; for(int j = 0; j < k; ++ j){ if (v == target && j != col) continue; int L = i, R = j; if (u != edges[id][0]) swap(L, R); int r = L * k + R; if (v == target){ tmp = min(tmp, 1ll * ecost[id][r]); }else{ tmp = min(tmp, dp[v][j] + pcost[v][j] + ecost[id][r]); } } dp[u][i] += tmp; } } }; dfs(st, st); for(int i = 0; i < k; ++ i){ ans = min(ans, dp[st][i] + pcost[st][i] + pcost[target][col]); } } cout << ans << '\n'; } return 0; }
闪耀循环
题目大意
给定个字符串 以及一个字符串,对于每个字符串,回答以下问题 :
依次选择若干个串,组成串 ,要求前一串的末尾字母和后一串的开头字母相同,第一个串的开头字母和最后一个串的末尾字母相同,字符串 的每个字母都在这些串组成的大串出现过。求最小代价。
代价为每个串的长度 的和。
解题思路
对于每个串,能够提供的信息就是首末字母和包含的字符串的字母情况。
建一张图,点是每个字母,对于一个串 ,连一条边 ,边权有两个,一个是串长度 ,另一个是包含的字符串 的字母情况,因为 ,可以二进制位压缩成一个数。
然后设 表示最终回到点 ,当前在点 ,已经获得到的字符串 的字母情况为 ,最终到点 且字母情况为满(即包括了 的所有字母)的最小代价。
然后从 开始 搜。枚举另边,不断消去第三维的值。
对于每个串的信息(包含串 字母的情况),答案就是,但由于我们是从终点开始搜的,对于第三维是能消去就先消去,但最后求答案是从起点考虑,虽然说经过了这条边,包含串 的字母情况变成 ,但其中有的字母可以后面的串得到,从终点搜到这里会已经消去,因此这里的应该遍历的所有子集,取最小值。
状态数是,但每个状态的连边实际上是有 的数量集,转移代价可能比较大,但或许实际上没那么大(或者数据水了),就过了。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const LL inf = 1e18; int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n; string t; cin >> n >> t; vector<int> id(26, -1); int cnt = 0; for(auto &i : t){ id[i - 'a'] = cnt; ++ cnt; } vector<vector<array<int, 3>>> edge(26); vector<array<int, 4>> query(n); for(int i = 0; i < n; ++ i){ string s; cin >> s; int st = s.front() - 'a'; int ed = s.back() - 'a'; int sign = 0; int len = s.size() - 1; for(auto &i : s){ if (id[i - 'a'] != -1){ sign |= (1 << id[i - 'a']); } } edge[ed].push_back({st, len, sign}); query[i] = {st, ed, sign, len}; } int up = (1 << cnt); vector<vector<vector<LL>>> dis(26, vector<vector<LL>>(26, vector<LL>(up, inf))); for(int i = 0; i < 26; ++ i){ dis[i][i][up - 1] = 0; priority_queue<array<LL, 3>> team; team.push({0, i, up - 1}); while(!team.empty()){ array<LL, 3> top = team.top(); team.pop(); LL distance = -top[0]; int u = top[1], sign = top[2]; if (dis[i][u][sign] != distance) continue; for(auto &e : edge[u]){ int v = e[0], len = e[1], si = e[2]; int nxtsign = (sign ^ (sign & si)); if (dis[i][v][nxtsign] > distance + len){ dis[i][v][nxtsign] = distance + len; team.push({-dis[i][v][nxtsign], v, nxtsign}); } } } } for(int i = 0; i < n; ++ i){ LL ans = inf; int sign = query[i][2]; for(int s = sign; s; s = (s - 1) & sign) ans = min(ans, dis[query[i][0]][query[i][1]][s]); ans = min(ans, dis[query[i][0]][query[i][1]][0]); if (ans == inf) ans = -1; else ans += query[i][3]; cout << ans << '\n'; } return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/17438739.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步