蓝桥杯1024第 2 场算法双周赛题解+Ac代码

提醒:篇幅可能有点长,为了方便,大家可以直接看目录快速查找想要的内容
1.新生【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第1张图片

思路:上届小桥获得了第14届总冠军,那么这届是第15届,直接输出15就是答案

2.铺地板【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第2张图片蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第3张图片

input:

4
7 6
2 2
12 8
1 12

output:

Yes
No
Yes
No

蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第4张图片

思路:思维/数学

1.对于每一块地板,如果能被凑出来,那么一定是2*3地砖组合出来的,无论2*3地砖怎么放都为6的倍数,故长为n,宽为m的地板,n*m%6==0一定成立

2.这里还要注意一个特判,就是如果长或宽为1(例如n=6,m=1),那么是6的倍数也无法满足要求(不能切割地砖)

Ac_code:java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();

        while(T-->0){
            int n=sc.nextInt(),m=sc.nextInt();

            if(n==1||m==1) System.out.println("No");
            else if(n*m%6==0) System.out.println("Yes");
            else System.out.println("No");
        }
    }
}

3.摆玩具【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第5张图片

input:

5 2
2 5 7 10 13

output:

8

蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第6张图片

思路:思维/贪心

简述题意:给一个长度为n的不下降数组,分成k个子数组,使子数组的极差的总和最小

1.对数组中相邻两数的差记为 f[0],f[1],f[2],f[3]....f[n-2],并对其累加记为sum(这里注意数组是不下降的),故f数组中的元素是>=0的。

2.此时可以发现sum就是将长度为n的数组的极差值。

3.但是题目有一个限制条件分成k个子数组,k等于1时,sum就是答案,k>=2时,此时贪心的想让相差大的分开多开一个子数组,以满足极差总和最小

举个栗子:

        h数组: 2 5 7 10 13

        f数组:3 2 3 3

        k等于2时,删除一个最大的数3,有3个3,故有三种情况分子数组的情况都满足条件,

        删除第一个3为:子数组分为 [2], [5 7 10 13],极差和为:0+8

        删除第二个3 为:子数组分为[2,5,7], [10,13],极差和为:5+3

        删除第三个3为:子数组分为[2,5,7,10],[13],极差和为:8+0

4.实现时对f数组排序,用总和减去f中(k-1)个最大的数

Ac_code:C++

#include 
#include 
#include 
#include 
#include 
#include
#include
#include 
#include 
#include
#include 


using namespace std;

typedef long long LL;
typedef pairPII;

#define x first
#define y second
#define ls u<<1
#define rs u<<1|1
#define all(ss) ss.begin(),ss.end()

int const mod1=998244353;   
int const mod2=1e9+7;
int const N=2e5+7;
int const INF=0x3f3f3f3f;

int T;
int n,k;
int a[N];
int f[N];

void solve(){
    scanf("%d%d",&n,&k);
    for(int i=0;i=0&&--k;i--)    sum-=f[i];  //减去(k-1)个最大的数
    cout<>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

4.通关【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第7张图片

input:

4 5
0 3 5
1 2 8
1 3 9
3 1 15

output:

3

蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第8张图片

蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第9张图片

思路:bfs+堆优化

简述题意:给一个以1为根的有向树和初始的经验,问最多能覆盖的结点为多少个

设与1相连且小于已有经验的结点集合记为S

1.,很容易想到一个暴力做法,就是对所有小于当前经验值的遍历一遍,看是否可以扩展树的大小,没有一个结点可以更新,直接输出答案,有一个结点可以更新,就可以再次进入循环遍历一遍S集合中的所有元素,但是这个方法会tle,故要优化。

2.我们换一个思考方式,把与1相连的结点但是还没有加入到集合S中的集合,记录到一个小根堆中,每次从小根堆中取出最小的闯关所需要经验值,看是否可以加入到集合中,可以加入集合中接着循环判断,不可以直接跳出循环输出答案

Ac_code:C++

#include 
#include 
#include 
#include 
#include 
#include
#include
#include 
#include 
#include
#include 


using namespace std;

typedef long long LL;
typedef pairPII;

#define x first
#define y second
#define ls u<<1
#define rs u<<1|1
#define all(ss) ss.begin(),ss.end()

int const mod1=998244353;   
int const mod2=1e9+7;
int const N=2e5+7;
int const INF=0x3f3f3f3f;

int T;
int n,m;
int a[N];

//v[i]:通过第i关所需要的最低经验值
//w[i]:通过第i关可以获得的经验值
int w[N],v[N];
vectorg[N];    //g[x]:x结点可以到达的所有结点

int bfs(){
    //first:通过第second关的最低经验值
    //second:结点编号
    priority_queue,greater>heap;   //小根堆
    heap.push({v[1],1});    
    int res=0;
    
    LL W=m; //一开始的经验值
    while(heap.size()){
        PII temp=heap.top();    heap.pop();
        int id=temp.y,min_v=temp.x;
        if(min_v>W) break;  //最低经验值都比已有经验值大,直接break
        W+=w[id];   //累加经验
        res++;  //闯关++
        for(int y:g[id])    heap.push({v[y],y});    //代表挑战的关入堆
    }
    return res;
}

void solve(){
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++){
        int fa; scanf("%d%d%d",&fa,w+i,v+i);
        g[fa].push_back(i);
    }
    
    cout<>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

5.串门【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第10张图片

input:

4
1 2 3
1 3 4
1 4 5

output:

15

蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第11张图片

思路:dfs求树的直径

简述题意:n个节点,n-1条边,选择一个结点为起点,输出访问所有结点的最小值

1.直接求很难,那么换个思路,每个结点必须遍历一次,那么每条边至少加一次,每条边加一次的总和记为sum,我们的目的就是让一些边遍历两次或者两次以上的可能尽量小,那么找出最长第一条路径max_len(注意是权值最大),让它只遍历一次,就能保证答案最小,那么sum-max_len就是遍历两次的边权值,答案就是sum+sum-max_len。

2.那么这么求sum,max_len呢?sum很好求,直接累加边权值即可,max_len其实是最大的一条路径(树的直径),那么就转化求树的直径即可。

3.求树的直径(最大的一条路径),这里有通解,就是随便找一个点,dfs一遍记录最大路径的值与结点编号,在用第一次记录的结点编号第二次dfs,更新最大的max_len即可。(这里感兴趣证明的可以查树的直径,还有注意一点就是树的直径不能有负权边,不然不能用此方法求解,要数形dp才能求解)

Ac_code:C++

#include 
#include 
#include 
#include 
#include 
#include
#include
#include 
#include 
#include
#include 


using namespace std;

typedef long long LL;
typedef pairPII;

#define x first
#define y second
#define ls u<<1
#define rs u<<1|1
#define all(ss) ss.begin(),ss.end()

int const mod1=998244353;   
int const mod2=1e9+7;
int const N=2e5+7;
int const INF=0x3f3f3f3f;

int T;
int n,m;
int a[N];
vectorg[N];    //first:结点编号,second:权值
int start;  //起始结点
LL max_len; //最长的一条路径

//dfs代码较短,第一次接触可能有点难,不过可以慢慢理解
void dfs(int x,int fa,LL val){
    if(max_len>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

6.神奇数【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第12张图片

input:

100
130

output:

5

思路:数位dp

简述题意:求[l,r]中神奇树的个数,神奇数的定义假设有n(n>=2)位数 s1,s2..sn,对数位s1+s2+..+sn-1的总和为sum,sum%sn==0则为神奇数,10<=-l<=r<=1e200

数位初学者入门这里推荐灵神:2376. 统计特殊整数 - 力扣(LeetCode)

1.数位dp的一般求法[1,x]中合法的数,但是我们要求的是[l,r],这里采用前缀和思想pre[r]-pre[l-1]

2.这里采用记忆化搜索,一般用4个参数,本题的4个参数为枚举到了第pos位,前面总和为sum,是否有限制limit,是否有前导0用num

3.枚举到最后一位时,枚举最后一位可以填那些数,满足记录合法数返回即可

Ac_code:C++

#include 
#include 
#include 
#include 
#include 
#include
#include
#include 
#include 
#include
#include 


using namespace std;

typedef long long LL;
typedef pairPII;

#define x first
#define y second
#define ls u<<1
#define rs u<<1|1
#define all(ss) ss.begin(),ss.end()

int const mod1=998244353;   
int const mod2=1e9+7;
int const N=2e5+7;
int const INF=0x3f3f3f3f;

int T;
int n,m;
int a[N];
string s;

//f[pos][sum]:枚举到了第pos位,前pos-1位的总和为sum,且没有限制的合法数
//200个9=200*9,这里多开7个
int f[207][1807];

int dfs(int pos,int sum,bool limit,bool num){
    int up=limit?s[pos]-'0':9;
    if(pos==(int)s.size()-1){
        int cnt=0;  //记录合法数
        for(int i=1;i<=up;i++)  //枚举最后一位填什么
            cnt+=sum%i==0;
        return cnt;
    }
    if(num&&!limit&&f[pos][sum]!=-1) return f[pos][sum];
    
    int res=0;
    //只有两位数了,就不能删除了
    //要不然可以删除最高一位
    if(pos<(int)s.size()-2&&!num)    res=dfs(pos+1,0,false,false);
    
    for(int i=num?0:1;i<=up;i++)
        res=(res+dfs(pos+1,sum+i,limit&&i==up,true))%mod1;
    
    if(num&&!limit) f[pos][sum]=res;    //记忆化
    return res;
    
}

int check(string s){
    int sum=0,last=s.back()-'0';
    if(last==0) return 0;   //最后一位不能为0
    for(int i=0;i>l>>r;
    
    s=l;
    memset(f,-1,sizeof f);
    int left=dfs(0,0,true,false);
    
    s=r;
    memset(f,-1,sizeof f);
    int right=dfs(0,0,true,false);
    
    //前缀和思想,但是多减了一个l,故要加上check(l)
    int ans=((right-left+check(l))%mod1+mod1)%mod1;
    cout<>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

7.小蓝的疑问【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第13张图片

蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第14张图片蓝桥杯1024第 2 场算法双周赛题解+Ac代码_第15张图片input:

7 4
2 9 8 7 8 6 4
1 2
1 3
2 4
2 6
3 5
3 7
1 2
1 1
3 1
2 1

output:

8
9
8
7

思路:

简述题意:给你一颗以1为根的有向树,q个询问,对于每个询问x,k,输出x结点为根x记为第0层,输出所有第k层结点的最大值

1,一个简单暴力的方法就是,对每个询问bfs求除第k层结点的值,取max输出即可

TLE_code:C++(超时代码,过了60%的样例)

打蓝桥杯的话,可以获得60%的分,也建议学一学

#include 
#include 
#include 
#include 
#include 
#include
#include
#include 
#include 
#include
#include 


using namespace std;

typedef long long LL;
typedef pairPII;

#define x first
#define y second
#define ls u<<1
#define rs u<<1|1
#define all(ss) ss.begin(),ss.end()

int const mod1=998244353;   
int const mod2=1e9+7;
int const N=2e5+7;
int const INF=0x3f3f3f3f;

int T;
int n,q;
int w[N];
vectorg[N];

//求以k为根的第k层结点的最大值
int bfs(int start,int k){
    int res=-1;
    queueq;    //first:结点编号,second:层数
    q.push({start,0});
    
    while(q.size()){
        PII temp=q.front(); q.pop();
        int id=temp.x,depth=temp.y;
        
        for(int y:g[id]){
            if(depth==k-1) res=max(res,w[y]);
            else    q.push({y,depth+1});
        }
    }
    
    return res;
}

void solve(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)   scanf("%d",w+i);
    for(int i=1;i>T;
    //scanf("%d",&T);
    
    init();
    
    while(T--){
        solve();
    }
	
	return 0;
}

最后一题正解后续更新.....感觉写不出正解了(更改博客时间2023/10/29)

描述有误,或者描述不好理解,欢迎指正

希望对你有用,欢迎关注,点赞,收藏

你可能感兴趣的:(蓝桥杯&数据结构与算法,蓝桥杯,职场和发展)