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

完全二叉树的权值

  • 基本思想:完全二叉树的层序遍历
  • 由于是完全二叉树,每层结点都是按顺序的,该层的结点数:
    $0<=x<=2^{n-1}$,(n为层数)
    如果该层结点数 $x<2^{n-1}$,则为最后一层
    设每一层有 num 个结点,k 为当前层级
  • 扫描一遍序列即可,由于结点绝对值最大为 $10^5$,最多有 $10^5$个点,最后一层和可能爆 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表示这种状态已经有了第1,2,7这三种糖果,用a数组保存
    既然是dp题,我们就直接上处理方法
状态表示:$f[i]$ 表示状态 i 下的最少方案数
状态转移方程:$$f[t]=min(f[t],f[j]+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;
}

组合数问题【待补】

未完待续…

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇