第十二届蓝桥杯省赛第二场C/C++B组题解

第十二届蓝桥杯省赛第二场C/C++B组题解

文章目录

  • 结果填空题
    • 填空题答案一览
    • A: 求余
    • B: 双阶乘
    • C: 格点
    • D: 整数分解
      • 暴力+剪枝+排列组合(运行几分钟)
      • 三重循环(一两秒)
      • 动态规划(1s内)
      • 隔板法
    • E: 城邦(最小生成树)
  • 程序设计题
    • F: 特殊年份
    • G: 小平方
    • H: 完全平方数(分解质因数)
    • I: 负载均衡(模拟、优先队列)
    • J: 国际象棋(状态压缩DP)

拿到试题解压一看,就这?啪的一下A题直接提交1,传统功夫A题点到为止。这题不用优化,那题直接暴力求解,半个小时不到就到了J题,很快啊!回头一检查,这题效率达不到、这题看错题意,全部丢分了,我大意了啊,他说他是简单题,他可不是简单题,排列组合、质因数分解、prim、优先队列、回溯,看来是有备而来!我劝年轻人好自为之,好好反思,以后不要再犯这样的聪明,贪图一时快…武林要以和为贵,要细心读题,仔细码题,谢谢朋友们!

成绩出来了,三题填空,三题半大题,弱省省一中上,要是E题没有看错权重就省一头了呀

结果填空题

填空题答案一览

题目 答案 分值
A: 求余 1 5
B: 双阶乘 59375 5
C: 格点 15698 10
D: 整数分解 691677274345 10
E: 城邦 4046 15

A: 求余

代码

#include 
using namespace std;

int main(){
    cout<< 2021%20 <<endl;
    return 0;
}
//1

B: 双阶乘

分析:高位上的数字对结果没有影响,取余截掉

代码:

#include 
using namespace std;

int main(){
    int num = 1;
    for(int i =2021;i>0;i-=2) num = (num * i) %1000000;
    cout<< num % 100000 <<endl;
    return 0;
}
//59375

C: 格点

分析: 根据题意,枚举所有可能符合的坐标,进行判断

代码:

#include 
using namespace std;

int main(){
    int res = 0;
    for(int i =1;i<=2030;i++){
        for(int j =1;j<=2030;j++){
                if(i * j <=2021) res++;
        }
    }
    cout<< res <<endl;
    return 0;
}
//15698

D: 整数分解

暴力+剪枝+排列组合(运行几分钟)

暴力枚举所有和为2021的五个正整数组合,再根据五个数的重复数排列组合

#include 
using namespace std;

#define ll long long

int main(){ 
    long long res = 0;
    int n =2021;
    for(int a = 1;a<=500;a++){
        for(int b = a;b<=1000;b++){
            for(int c = b;c<=1500;c++){
                for(int d = c;d<=2000;d++){
                    int e = n - a - b - c - d, t = 0;
                    if(e >= d){
                        //五个相等
                        if(a==e) t =1;
                        //四个相等
                        else if((a==d) || (b==e)) t =5;
                        //三个相等 + 两个相等
                        else if((a==c && d == e) || (a==b && c==e))  t =10;
                        //三个相等                        
                        else if((a==c) || (b==d) || (c==e)) t= 20;
                        //两对相等
                        else if((a==b && c==d) || (a==b && d==e) || (b==c && d ==e)) t= 30;
                        //一对相等
                        else if(a==b || b==c || c==d || d==e) t = 60;
                        //全不相等
                        else t = 120;
                    }

                    res += t;
                }
            }
        }
    }
    cout<< res <<endl;
    return 0;
}
//691677274345

三重循环(一两秒)

三重循环枚举三个数,差是m,和为m的两个正整数可能是m-1种

#include 
using namespace std;

int main()
{
	ll ans = 0;
	int n = 2021;
	for(int i = 1;i < n; ++i) {
		for(int j = 1;j < n; ++j) {
			for(int k = 1;k < n; ++k) {
					int m = 2021 - i - j - k;
					if(m <= 1) break;
					ans += m - 1;
			}
		}
	}
	cout<< ans <<endl;
	return 0;
}

动态规划(1s内)

1-2021,取五个数的和是2021的可能数

d p [ i ] [ j ] dp[i][j] dp[i][j]表示 i i i个数和为j的可能数

k k k表示 d p [ i − 1 ] [ j − k ] dp[i-1][j-k] dp[i1][jk]加上一个数 k k k,就是 d p [ i ] [ j ] dp[i][j] dp[i][j]的所有可能

d p [ i ] [ j ] = { d p [ i ] [ j ] + d p [ i − 1 ] [ j − k ] ( j > k ) 0 dp[i][j] = \begin{cases} dp[i][j] +dp[i-1][j-k](j>k)\\0 \end{cases} dp[i][j]={dp[i][j]+dp[i1][jk](j>k)0

代码

#include 
using namespace std;

#define ll long long

int main(){
    ll dp[6][2022];
    memset(dp, 0, sizeof dp);
    for(int i = 1;i<2022;i++) dp[1][i] = 1;
    for(int i =2;i<=5;i++)
        for(int j =1; j<2022;j++)
            for(int k = 1;k<2022;k++){
                if(j-k > 0) dp[i][j] += dp[i-1][j-k];
            }
    cout<< dp[5][2021];
    return 0;
}
//691677274345

隔板法

C(2,6)

#include 
using namespace std;

#define n 20210509
#define ll long long
ll dp[20210519];

int main() {
	cout<< 1ll*2020*2019*2018*2017/4/3/2<<endl;
	return 0;
}
//691677274345

E: 城邦(最小生成树)

分析:构建邻接矩阵,最小生成树套模板(prim)

代码

#include 
using namespace std;

#define maxn 2030
#define INF 0x3f3f3f3f
int e[maxn][maxn];
int cost[maxn];
bool used[maxn];
int V = 2021;

//prim
int prim(){
    for(int i =1;i<=2021;i++){
        cost[i] = INF;
        used[i] = false;
    }
    cost[1] = 0;
    int res = 0;
    while (1)
    {
        int v = -1;
        for(int u = 1;u<=V;u++){
            if(!used[u] && (v==-1 || cost[u] < cost[v])) v = u;
        }
        if(v == -1) break;
        used[v] = 1;
        res += cost[v];
        for(int u = 1;u<=V;u++){
            cost[u] = min(cost[u], e[v][u]);
        }
    }
    return res;
}
//获取权重
int get(int a, int b){
    int res = 0;
    while(a || b){
        if(a%10 != b%10) res+=a%10 + b%10;
        a/=10, b/=10;
    }
    return res;
}
void solve(){
   //邻接矩阵
    memset(e, 0, sizeof e);
    for(int i =1;i<=2021;i++)
    {
        for(int j =1;j<=2021;j++)
        {
            if(i == j) continue;
            e[i][j] = e[j][i] = get(i , j);
        }
    }
    prim();
    cout<< prim() <<endl;
}
int main(){
    solve();
    return 0;
}
//4046

程序设计题

F: 特殊年份

分析:根据题意,求各位的数字,进行判断

代码:

#include 
#include 
using namespace std;

#define maxn 10

int a[maxn];
int main(){

    for(int i =0;i<5;i++) cin>>a[i];

    int res = 0;
    for(int i=0;i<5;i++){
        int q = a[i] /1000;
        int b = a[i] %1000 / 100;
        int s = a[i] %100 / 10;
        int g = a[i] %10;
        if(q == s && g == b +1) {
            res ++;
        }
    }
    cout<< res;
    return 0;
}

G: 小平方

分析:暴力枚举,数据规模小(1 ≤ n ≤ 10000)

代码:

#include 
using namespace std;

int main(){
    int n;
    cin >> n;
    int res = 0;
    for(int i =1;i<n;i++){
        if( (i*i%n) < (n /2.0)) res++;
    }
    cout<< res;
    return 0;
}

H: 完全平方数(分解质因数)

分析:参考大佬的题解。。
对n进行质因数分解,将奇数个的所有质因数的乘积就是答案。

举例子:对某个数质因数分解的结果是:2 2 2 3 3 3,那么这个数乘上2*3就是完全平方数

举例子:某个数的质因数分解的结果是:2 2 3 3 4 4, 那么这个数本身就是完全平方数,答案是1;

代码:

#include 
using namespace std;

#define ll long long

int main(){
    ll n;
    cin>>n;
    ll res =1;
    for(int i =2;i<=1000010;++i){
        if(n<2) break;
        ll t = 0;
        while(n%i==0){
            ++t;
            n/=i;
        }
        if(t%2==1) res *= i;
    }
    if(n > 1) res *= n;
    cout << res;
    return 0;
}

I: 负载均衡(模拟、优先队列)

分析
代码要模拟的功能总结:枚举所有任务,对于当前每个任务,消耗算力并将该算力倒计时,直到任务结束返回算力(当前任务消耗d算力c秒)。

由于时间的数据范围10^9,枚举所有的时间会超时。怎么进行算力倒计时结束后将算力恢复,决定了这道题的时间复杂度

优先队列小顶堆维护任务占用的算力,算力释放的时间 (a+c) 为优先级。每次分配任务前,先释放算力,如果足够分配当前任务,将该任务消耗的算力压入优先队列。

时间复杂度O(m*log(m)),应该能过全部数据吧。

代码:

#include 
using namespace std;

#define maxn 200010
int n, m;
int arr[maxn];
int a, b, c, d;
struct Node
{
    int endTime, id , val;
    bool operator< (const Node &node)const {
        return endTime > node.endTime;
    }
};

priority_queue<Node> q;
//释放算力
void getd(){
    while(!q.empty()){
        Node t= q.top();
        if(t.endTime > a) break;
        q.pop();
        int i =t.id, v = t.val;
        arr[i] += v;
    }
}
//消耗算力
void task(){
    getd();
    if(arr[b] >= d){
        q.push({a+c, b ,d});//算力进入恢复队列
        arr[b] -= d;
        cout<< arr[b];
    }else{
        cout<<"-1";
    }
}
int main(){
    cin>>n>>m;
    memset(arr, 0 , sizeof arr);
    for(int i =1;i<=n;i++) cin>>arr[i];
    while(m--){
        cin>> a>> b >>c>>d;
        task();
        if(m>0) cout<<endl;
    }
    return 0;
}

J: 国际象棋(状态压缩DP)

每一行只受限于上两行的状态

#include 
using namespace std;

const int N = 110, M = 1<<6,K = 21, MOD = 1e9+7;
int n, m ,k;
int dp[N][M][M][K];
#define ll  long long

int getCnt(int num){
    int res = 0;
    while (num)
    {
        res ++;
        num &= (num-1);
    }
    return res;
}
int main(){
    cin >> n >> m >> k;
    dp[0][0][0][0] = 1;
    for(int i =1;i<=m;i++){
        for(int a = 0;a< 1<<n;a++){
            for(int b = 0; b< 1<<n;b++){
                if(a & (b << 2) || b & (a << 2)) continue;
                for(int c = 0;c < 1<<n; c++){
                    if(b & (c << 2) || c & (b << 2)) continue;
                    if(a & (c << 1) || c & (a << 1)) continue;
                    int t = getCnt(a);
                    for(int j = t;j<=k;j++){
                        dp[i][a][b][j] += dp[i-1][b][c][j-t] % MOD;
                    }
                }
            }
        }
    }
    ll res = 0;
    for(int i =0;i<1<<n;i++)
        for(int j =0;j< 1<<n;j++)
            res += dp[m][i][j][k]%MOD;

    cout<< res %MOD <<endl;
    return 0;
}


有错的地方请指正

参考视频讲解:第十二届蓝桥杯(第二场)C++ B组讲解
参考博客:第十二届蓝桥杯大赛软件赛省赛第二场题解
参考视频讲解:第十二届蓝桥杯C++ B组第二场省赛题目讲解

你可能感兴趣的:(蓝桥杯,算法,动态规划)