第十届蓝桥杯省赛编程题题解(C++A组)

完全二叉树的权值

在这里插入图片描述

  • 基本思想:完全二叉树的层序遍历
  • 由于是完全二叉树,每层结点都是按顺序的,该层的结点数:
    0 < = x < = 2 n − 1 , ( n 为层数) 0<=x<=2^{n-1},(n为层数) 0<=x<=2n1,n为层数)
    如果该层结点数 x < 2 n − 1 x<2^{n-1} x<2n1,则为最后一层
    设每一层有num个结点,k为当前层级
  • 扫描一遍序列即可,由于结点绝对值最大为 1 0 5 10^5 105,最多有 1 0 5 10^5 105个点,最后一层和可能爆int,所以这里用long long.
  • 代码如下
#include <iostream>

using namespace std;

const int N=100010;

int a[N];
int n;

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    
    int d=0;
    long long res=-0x3f3f3f3f;
    for(int i=0,k=1,num=1;i<n;i+=num,k++,num*=2)
    {
        long long sum=0;
        for(int j=i;j<i+num&&j<n;j++) sum+=a[j];//层序遍历
        if(sum>res) res=sum,d=k;
    }
    
    cout<<d<<endl;
    
    return 0;
}

外卖店优先级

  • 同理,直接暴力会TLE
算法步骤:
  1. 根据订单时间对所有订单排序,为什么要排序呢,因为排序后处理每一个外卖店都是往后的时间,可以轻易得到上一次外卖店的订单时间
  2. 根据上一次的订单时间计算优先级,用数组a表示当前优先级数量,数组b表示上一次的订单时间,st标记该外卖点是否在缓存中
  3. 遍历所有订单,再处理最后一次时间T产生的优先级减少
  • 代码如下
#include <iostream>
#include <algorithm>

using namespace std;

typedef pair<int,int>PII;
#define t first
#define id second

const int N=100010;

int a[N],b[N];
int n,m,T;
PII s[N];
bool st[N];

int main()
{
    cin>>n>>m>>T;
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        s[i]={a,b};
    }
    sort(s,s+m);
    
    for(int i=0;i<m;i++)
    {
        int k=s[i].id;
        
        if(b[k]!=s[i].t)//不是同一时间的订单
        {
            a[k]=max(0,a[k]-(s[i].t-b[k]-1));
            if(a[k]<=3) st[k]=false;
            //这里要注意判断一定要加在下一个语句前面,不要在后面
        }
        
        a[k]+=2;
        if(a[k]>5) st[k]=true;
        
        b[k]=s[i].t;
    }
    
    for(int i=1;i<=n;i++)
    {
        a[i]-=T-b[i];
        if(a[i]<=3) st[i]=false;
    }
    
    int res=0;
    for(int i=1;i<=n;i++) res+=st[i];
    
    cout<<res<<endl;
    
    return 0;
}

修改数组

  • 这题容易想到的是直接模拟一遍,用一个st数组标记是否用过该数,依次扫描输入的数往下递推,但是这样是妥妥地TLE.
因此我们考虑用另一种方法—并查集
算法步骤:
  • 基本思路:用p[x]储存这个x的下一个可用的数
  1. 对并查集初始化,p[i]=i;
  2. 对输入的数进行判断,通过find(x)找到该数的下一个可用的数,最后这个数的p[x]=x+1,表示x已经用过了,下一个只能用x+1.
  3. 每轮操作都输出数即可,也可以用离线做法
  • 代码如下:
#include <iostream>

using namespace std;

const int N=100010,M=1100010;

int n;
int p[M];

int find(int x)
{
    if(x==p[x]) return x;
    return p[x]=find(p[x]);//并查集能行优化得益于这里
}

int main()
{
    cin>>n;
    for(int i=1;i<=M;i++) p[i]=i;//init();
    for(int i=0;i<n;i++)
    {
        int x; scanf("%d",&x);
        x=find(x);
        printf("%d ",x);
        p[x]=x+1;
    }
    
    return 0;
}

糖果

  • 糖果种数少,每种糖果只有选了和未选两种状态,因此我们可以考虑状压dp,经过验证,可行!
  • 1000011 1000011 1000011表示这种状态已经有了第 1 , 2 , 7 1,2,7 1,2,7这三种糖果,用a数组保存
    既然是dp题,我们就直接上处理方法
状态表示: f [ i ] f[i] f[i]表示状态i下的最少方案数
状态转移方程: f [ t ] = m i n ( f [ t ] , f [ j ] + 1 ) f[t]=min(f[t],f[j]+1) f[t]=min(f[t],f[j]+1)
结果表示: f [ ( 1 < < m ) − 1 ] f[(1<<m)-1] f[(1<<m)1]
  • 代码如下:
#include <iostream>
#include <cstring>

using namespace std;

const int M=1<<20;

int a[M],f[M];
int n,m,k;

int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<k;j++)//状态集合到a[i];
        {
            int x; scanf("%d",&x);
            a[i]|=1<<x-1;//该包糖果的种类数累计
        }
    }
    
    memset(f,0x3f,sizeof f);
    f[0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<1<<m;j++)
        {
            int t=j|a[i];
            f[t]=min(f[t],f[j]+1);
        }
    }
    
    if(f[(1<<m)-1]==0x3f3f3f3f) cout<<-1<<endl;
    else cout<<f[(1<<m)-1]<<endl;
    
    return 0;
}

组合数问题【待补】

未完待续…

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值