蓝桥杯-反向思维

2022年(第⼗三届)省赛,lanqiaoOJ题号2117

【题⽬描述】⼩明砍⽵⼦,他⾯前有n根⽵⼦排成⼀排, ⼀开始第i根⽵⼦的⾼度为hi。他觉得⼀根⼀根地砍太慢 了,决定使⽤魔法来砍⽵⼦。魔法可以对连续的多根相同 ⾼度的⽵⼦使⽤,假设这些⽵⼦的⾼度均为H,那么使⽤ ⼀次魔法可以把这些⽵⼦的⾼度都变为\sqrt{|\frac{H}{2}|+1},其中\left | H \right |表示对H向 下取整。⼩明想知道他最少要使⽤多少次魔法可以让所有 的⽵⼦的⾼度都变为1。

【输⼊格式】第⼀⾏输⼊⼀个正整数n,表示⽵⼦的根 数。第⼆⾏输⼊n个⽤空格分开的正整数hi,表示每棵⽵ ⼦的⾼度。 【输出格式】输出⼀个整数表示答案。

【输⼊样例】 6 2 1 4 2 6 7

【输出样例】 5

【评测⽤例规模与约定】对于20%的测试数据, n≤1000,hi≤106;对于100%的测试数据,n≤2×105, hi≤1018。

先尝试⽤暴⼒法解题。先找到最高的竹子,记录高度砍一次。然后找是不是有和记录高度一样高的,如果有就一起砍。以此类推直到所有竹子高度都是1.。

注意这里竹子的顺序是可以变换的,也就是说只要两根竹子高度相同那么它们就可以一起砍。

下⾯⽤Python编写以暴⼒法求解的代码。代码的时间复杂 度:从左到右遍历⼀次的复杂度为O(n),可能会遍历很多 次,总复杂度⼤于O(n^{2})。只能通过20%的测试数据。

python

from math import *
n=int(input())
a=list(map(int,input().split()))
# n=6
# a=[2,1,4,2,6,7]
ans=0
while True:
	idx=0
	for i in range(n):
		if a[i]>a[idx]:
			idx=i
	if a[idx]==1:	break#检查是否全部竹子的高度都是1
	val=a[idx]#记录最高的竹子高度
	for i in range(idx,n):#从最高的竹子往后遍历(因为前面的竹子都比它矮)
		if a[i]!=val:	break#有不同高的竹子则退出
		a[i]=floor(sqrt(floor(a[i]/2)+1))#砍后的高度
	ans+=1#记录为砍了一次
print(ans)

有⼀些题解⽤了优先队列,不过仍不能通过100%的测试数据。 本题的正解是模拟,不需要什么算法就能计算出最少砍⼑次数的 步骤如下。

①计算最多砍多少次,计算将每根⽵⼦砍到⾼度为1需要砍多少次,将所有⽵⼦被砍次数相加得到⼀个总数,记为sum。

②记录每根⽵⼦每次被砍后的新⾼度。

③⽐较任意两个相邻的⽵⼦,看它们是否有相同的⾼度,如果有相同的⾼度,则这两根⽵⼦接下来可以⼀起砍,从⽽少砍⼀次, sum减1。

④⽐较结束后,ans就是答案。

C++

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=10;
int main(void){
    // 6 2 1 4 2 6 7
    int n,x,top=0,sum=0;
    scanf("%d",&n);
    ll a[n];
    ll h[n][M];
    ll stack[M];
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        a[i]=x;
        //这里是O(n)的复杂度
        ll height=a[i];
        top=0;//top当前位置没有数
        while(height>1){
            stack[top++]=height;//记录高度
            height=sqrt(height/2+1);//砍一刀
            sum++;
        }
        for(int j=0,k=top-1;j<top;j++,k--){
            h[i][j]=stack[k];
        }
    }
    for(int i=1;i<n;i++){
        for(int j=0;j<M;j++){
            if(h[i][j]&&h[i-1][j]&&h[i][j]==h[i-1][j]){
                sum--;
            }
        }
    }
    printf("%d",sum);
}

1.注意高度范围,凡是存储竹子高度的数组或数据都要用long long类型

2.对于初始高度等于1的竹子无需加入h二维数组中,所以while的条件是height>1

3先记录高度再砍一刀,注意顺序避免出错

理解代码:

假如在竹子上画出刻度线,每一根刻度线标志着被砍一次。那么我们记下所有刻度线的数量,也就是初始的sum。然后比较所有竹子上的最靠近底部的第一根刻度线(从顶部开始也行),如果找到两个位置相同的就sum-1,三个就sum-2..。然后再接着往上走,找倒数第二根刻度线,比较...。一直到所有刻度线都比较完,那么sum就是最后的结果。.

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雾渐起

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值