第十一届蓝桥杯大赛软件类省赛
总体感觉,白给
试题链接: https://pan.baidu.com/s/1zas4b7HMJzQ9FK6LbWoJaA
提取码:p8z9
试题A:跑步训练
因为答案要求的是以秒为单位,所以在运行的时候直接换成秒就行了
试题 B: 纪念日
这。。。能用计算器算是我没想到的。。。
简单实现一个日期类,从1921.7.23
,一天一天的加,直到2020.7.1
。。。。。。。。
#include <iostream>
using namespace std;
class Data {
public:
Data(int y,int m,int d) {
_y = y;
_m = m;
_d = d;
}
int check(Data& v) {
int ret = 0;
cout<<_y<<" "<<_m<<" "<<_d<<endl;
while(isS(v) == false) {
*this += 1;
ret++;
//cout<<_y<<" "<<_m<<" "<<_d<<endl;
}
cout<<_y<<" "<<_m<<" "<<_d<<endl;
return ret;
}
int Md(int y,int m) {
static int M[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int ret = M[m];
if(m == 2 && isY(y)) ret++;
return ret;
}
bool isY(int y) {
if((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) return true;
return false;
}
void operator+=(int v) {
_d += v;
int md = Md(_y,_m);
if(_d > md) {
_d = 1;
_m ++;
if(_m == 13) {
_m = 1;
_y ++;
}
}
}
bool isS(Data& d){
return _y == d._y && _m == d._m && _d == d._d;
}
private:
int _y;
int _m;
int _d;
};
int main() {
Data d(1921,7,23);
Data d1(2020,7,1);
cout << d.check(d1) * 24 * 60 << endl;
return 0;
}
试题 C: 合并检测
就想到了放弃两个字
试题 D: REPEAT 程序
虽然用例看上去很少,但是文件中的程序有一千行,手算肯定不行
直观发现,REPEAT 5:
和 A = A + 8
的长度是一样的。所以说,当我们直接从文件中读取一行数据,如果他的长度和之前某个状态相同,说明这是处于同一级的。
我们可以利用栈
后进先出的思想来模拟一下
#include <fstream>
#include <iostream>
#include <string>
#include <stack>
#include <map>
using namespace std;
using key = pair<int,int>;
int main() {
fstream fp("D:\\桌面\\prog.txt");
if(fp == nullptr) cout<< " open error" << endl;
stack<key> st;
st.push(make_pair(1,0));
string str("");
getline(fp,str);
int ans = 0;
while(getline(fp,str)) {
//cout<< str << endl;
int n = str.size();
int num = str[n-2] - '0';
while(true) {
auto t = st.top();
if(t.second >= n) { // 上一级 同一级
st.pop();
} else if(t.second < n) { // 下一级
num *= t.first;
break;
}
}
if(str[n-1] == ':') { // REPEAT 2:
st.push(make_pair(num,n));
} else { // A = A + 4
int v = st.top().first;
ans += v * (str[n-1] - '0');
//cout << v << " " << str[n-1] << endl;
}
}
cout << ans << endl;
return 0;
}
试题 E: 矩阵
动态规划,dp[i][j]
,第一行放i
个数,第二行放j
个数,有dp[i][j]
种情况
注意:放第一行的时候,他所在列的第二行不可以有数据。
试题 F: 整除序列
#include <iostream>
using namespace std;
int main() {
long long n = 0;
cin >> n;
while(n != 0) {
cout << n << " ";
n /= 2;
}
cout << endl;
return 0;
}
试题 G: 解码
。。。。。
#include <iostream>
#include <string>
using namespace std;
int main() {
string str("");
cin >> str;
string ans("");
int p = 0;
int n = str.size();
while(p < n) {
char ch = str[p++];
int cnt = 0;
while(p < n && (str[p] <= '9' && str[p] >= '0')) {
cnt += cnt * 10 + (str[p++] - '0');
}
if(cnt == 0) {
ans += ch;
continue;
}
for(int i = 0; i < cnt; i++) {
ans += ch;
}
}
cout << ans << endl;
return 0;
}
试题 H: 走方格
老动态规划了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int N = 50;
const int M = 50;
int dp[N][M];
int n,m;
int main() {
cin >> n >> m;
memset(dp,0x00,sizeof dp);
dp[1][1] = 1;
for(int i = 1; i <= n ; i++)
for(int j = 1; j <= m; j++) {
if(((i & 1) == 0) && ((j & 1) == 0)) continue;
dp[i][j] += dp[i-1][j] + dp[i][j-1];
}
cout << dp[n][m] << endl;
return 0;
}
试题 I: 整数拼接
暴力法
直接暴力解决时,只能过部分用例,还需要将使用unsigned long long
类型,不然1e9 * 1e9
可能会超出long long
数据的范围
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int nums[N];
int len[N];
ll ans = 0;
int n,k;
int GetLen(int num) {
int ret = 0;
while(num != 0) {
num /= 10;
ret ++;
}
return ret;
}
// 超时 typedef unsigned long long ll;
void slove() {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j ++)
if(i != j && ( (nums[i] * (ll)pow(10,len[j]) + nums[j]) % k == 0 ) ) {
ans++;
}
}
}
int main() {
cin >> n >> k;
memset(nums,0x00,sizeof nums);
for(int i = 0; i < n; i++) {
cin >> nums[i];
len[i] = GetLen(nums[i]);
}
slove();
cout << ans << endl;
return 0;
}
记忆化
在之前的暴力中,重复做了很多,我们可以对第二层循环进行优化,(nums[i] * 10^len[j] + nums[j] )% k
,等价于nums[i] * 10 ^ len[j] % k + nums[j] % k
。
所以,优化的方案中,我们可以对之前出现的数据,记录下每个 10 ^ n
( 0 < n < 11)中,对k取余后的数的次数
cnt[i][j]
,表示10 ^ i
中,j = nums[idx] * 10 ^ i % k
,出现的次数
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int nums[N];
int len[N];
ll ans = 0;
int n,k;
int GetLen(int num) {
int ret = 0;
while(num != 0) {
num /= 10;
ret ++;
}
return ret;
}
int cnt[11][N];
void slove() {
memset(cnt,0x00,sizeof cnt);
for(int i = 0; i < n; i++) {
ans += cnt[len[i]][(k - nums[i] % k) % k];
long long power = 1;
for(int j = 0; j < 11; j ++) {
cnt[j][power * 1ll * nums[i] % k] ++;
power = power * 10 % k;
}
}
}
int main() {
cin >> n >> k;
memset(nums,0x00,sizeof nums);
for(int i = 0; i < n; i++) {
cin >> nums[i];
len[i] = GetLen(nums[i]);
}
slove();
reverse(nums,nums + n);
reverse(len,len + n);
slove();
cout << ans << endl;
return 0;
}
试题 J: 网络分析
暴力搜索,超时
直接用unordered_map
,对路径进行存储,每次进行搜索更新,估计只能50%
上下的用例,因为做的重复工作太多了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <unordered_map>
#include <set>
using namespace std;
const int N = 10010;
unordered_map<int,set<int> > um;
int n,m;
int op,a,b;
int ans[N];
vector<bool> vis;
void dfs(int u,int num) {
if(vis[u] == true) return ;
vis[u] = true;
ans[u] += num;
for(auto& v : um[u]) {
dfs(v,num);
}
}
int main() {
cin >> n >> m;
for(int i = 0; i < m; i++) {
cin >> op >> a >> b;
if(op == 1) {
um[a].insert(b);
um[b].insert(a);
}
else if(op == 2) {
vis = vector<bool> (n+1,false);
dfs(a,b);
}
}
for(int i = 1; i <= n; i++) cout << ans[i] << " ";
return 0;
}
并查集 + 差分
以链式前向星
的方式存储边的集合,对于需要合并的两个点来说,如果他们本身不在一个集合中,那么我们新构造一个root
点,他作为这两个点的新的根节点。我们可以通过这个根节点,遍历完整颗子树。
利用差分的思想,从我们新建的root
根节点开始,从上向下遍历,每次孩子节点的 ans[ch]
的结果,需要加上父亲节点的结果(差分)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int N = 200010, M = N << 1; // N 是点,M 是可能构成的边
// h[i],表示该链式构成边的第一个点
// e[i],表示边的终点
// ne[i],下一条边的终点
int h[N], e[M], ne[M], idx;
int root;
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int n,m;
int p[N];
int find(int x) {
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void merge(int a,int b) {
int fa = find(a);
int fb = find(b);
if(fa == fb) return ;
p[fa] = p[fb] = root;
add(root,fa);
add(root,fb);
root++;
}
int ans[N];
void dfs(int ch,int fa) {
ans[ch] += ans[fa];
for(int v = h[ch]; ~v ; v = ne[v]) // 遍历以 ch 为起点的边的编号 v,边的终点为e[v]
dfs(e[v],ch);
}
int main() {
cin >> n >> m;
for(int i = 1; i <= 2 * n; i++) p[i] = i;
memset(h, -1, sizeof h);
root = n + 1;
while(m --) {
int op,a,b;
cin >> op >> a >> b;
if(op == 1) {
merge(a,b); // 合并a,b边
} else if(op == 2) {
int fa = find(a); // 差分
ans[fa] += b;
}
}
for(int i = n + 1; i < root; i++)
if(p[i] == i) dfs(i,0);
for(int i = 1; i <= n; i++) cout << ans[i] << " ";
return 0;
}