第十一届蓝桥杯大赛软件类省赛,7月

总体感觉,白给
试题链接: 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;
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值