首页 > 试题广场 >

小红的小踏前斩

[编程题]小红的小踏前斩
  • 热度指数:821 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
n个怪物排成一排,每只怪物的血量为a_i
小红有一个技能:小踏前斩。效果是:选择一只怪物,对这只怪物造成1点伤害,并发出剑气,对下一个怪物造成2点伤害。(注:若下一个怪物已死亡,则剑气会打在尸体上,并不会向后穿透)。
小红可以对尸体发出踏前斩,剑气同样可以溅射到后面的怪物。但小红无法对第一个怪物前面的空气发出踏前斩用来溅射第一个怪物。
小红想至少击杀2只怪物,她想知道自己需要最少发出多少次小踏前斩?

输入描述:
第一行输入一个正整数n,代表怪物的数量。
第二行输入n个正整数a_i,代表每只怪物的血量。
2\leq n\leq 200000
1\leq a_i \leq 10^9


输出描述:
一个正整数,代表踏前斩的最小次数。
示例1

输入

2
2 6

输出

3

说明

对第一个怪物释放两次踏前斩,然后对它的尸体再放一次踏前斩。
示例2

输入

4
2 3 2 5

输出

2

说明

对第一个怪物放一次踏前斩,再对第二个怪物放一次踏前斩即可。此时第二只怪物、第三只怪物死亡。
import math

n = int(input())
nums = list(map(int, input().split()))

min1, min2 = nums[0], 1e9

res = max(math.ceil(nums[1] / 2), nums[0])

for i in range(1, n-1):
    a, b = nums[i], math.ceil(nums[i+1]/2)
    if b > a:
        res = min(b, res)
    else:
        res = min(b+math.ceil((a-b)/2), res)
    c = math.ceil(nums[i]/2)
    if c < min1:
        min2 = min1
        min1 = c
    else:
        if c < min2:
            min2 = c

print(min(min1+min2,res))
发表于 2023-03-12 23:43:04 回复(1)
#python代码
#踏前斩最小数量:分两种情况讨论,两个相邻的怪物或者最小血量的两个怪物
#核心思想:1.如果第二个怪物的血量大于第一个怪物血量的两倍,如2,5,这时最小次数主要看
#第二个怪物的血量,最小次数为(5+1)//2
#2.如果第二个怪物的血量小于等于第一个怪物血量的两倍,如2,3,这时首先对第一个怪物释放
#技能(因为这样可以同时打到两个怪物),第二个怪物死后,为消灭第一个怪物,对第一个怪
#物的前一个怪物释放技能打死第一个怪物。
#注意i==0时要分类讨论
#求最小的两个值时,首先给min1赋值为a[0],因为后面循环i从0变到n-2,那么(i+1)刚好就
#是除了第一个元素之外的n-1个元素
n = int(input())
a = list(map(int,input().split()))
min1 = a[0]
min2 = float('inf') 
ans = float('inf')
for i in range(n-1):
    if i == 0:
        if a[1] <= 2*a[0]:
            count = a[0]
        else:
            count = (a[1]+1)//2
    else:
        if a[i+1] > 2*a[i]:
            count = (a[i+1]+1)//2
        else:
            count = (a[i+1]+1)//2+(a[i]-(a[i+1]+1)//2+1)//2
    ans = min(ans,count)
    m = (a[i+1]+1)//2    #求杀死a[i+1]怪兽的最小踏前斩次数
    if m < min1:
        min2 = min1
        min1 = m
    elif m < min2:
        min2 = m
print(min(ans,min1+min2))

发表于 2023-04-04 16:21:46 回复(1)

枚举法

#include <bits/stdc++.h>
using namespace std;

int getDeadCnt(int val, int idx) {
    if (val <= 0) return 0;
    else if (idx == 0) return val;
    else return (val / 2 + (val % 2));
}

int main() {
    int n;
    cin >> n;
    vector<int64_t> v(n, 0);
    for (auto& e : v) cin >> e;
    int ret = v[0] + v[1];
    int idx = 0;
    for (int i = 1; i < v.size(); ++i) {
        int cnt = getDeadCnt(v[i], i);
        int preCnt = getDeadCnt(v[i - 1] - cnt, i - 1);
        int tmpCnt = preCnt;
        if (idx < i - 1) {
            if (idx == 0) tmpCnt = v[idx];
            else tmpCnt = v[idx] %2 + v[idx]/2;
        }
        ret = min(ret, cnt + min(preCnt, tmpCnt));
        if (v[idx] >= v[i]) idx = i;
    }
    std::cout << ret << std::endl;
}
// 64 位输出请用 printf("%lld")
发表于 2023-03-16 01:26:15 回复(2)
#include <iostream>
using namespace std;

int main() {
    int length;    
    cin  >> length;        //输入数组长度

    int num[200000] = {};  
    int min1 = 2147483646 ;
    int min2 = 2147483646;    
    int sum_min = 2147483646;   //要比小, 所以初始值调大些, 如果比这些数小就替换.
    cin>> num[0];               //第一个数不参与循环

    int sum = 0;            
    int a,b= 0;                //用于求和,记录
   
    for(int i = 1 ; i<length ; i++) //第一个数已经放进去了所以 i 从 1 开始
    {
        cin >> num[i];        //输入
        if(num[i]<min1)      
        {
            min2 = min1;
            min1 = num[i];    //如果当前值比最小值还小, 那么当前值为新的最小值, 原最小值变为次小值
        }
        else if(num[i]<min2)
        {
            min2 = num[i];    //如果当前值大于等于最小值, 但是又小于次小值, 那么当前值变为次小值
        }

        sum = num[i]+num[i-1];  //求当前值与上一个值的和
        if(i ==1)              
        {
            continue;           //最前面的两个数的和舍弃
        }
        if(sum<sum_min)         //如果求得和比最小的和还要小,就替换
        {
            sum_min = sum;
            a = num[i-1];      //并且记录下这两个数字
            b = num[i];
        }  
    }

    //cout <<min1<<" "<<min2<<" "<<sum_min<<" "<<a<<" "<<b<<endl;
    int count1 = (min1+1)/2 + (min2+1)/2;   //如果没有特殊情况, 不连续的两个怪都用剑气打死
      if(((num[0]+1)/2)<((min2+1)/2))       //如果第一个数太小, 以至于直接打花费的次数比用剑气打其他数更少,那就打第一个
    {
        count1 = num[0] + (min1+1)/2;       //比最小值花费次数少, 原最小值就是新的次小值
                                            //比次小值花费次数少, 也还是加上最小值的刀数
    }
    int count2 = (2*a<=b)?((b+1)/2):((a-(b+1)/2+1)/2+(b+1)/2);  //特殊情况都排除了, 肯定是连续的两个值
                                                                //先用剑气把后面的打死,如果前面还没死就再补刀
                                                                //已经保证了前面还有数, 所以都用剑气打

    int count3 = (num[1]>=2*num[0])?((num[1]+1)/2):(num[0]);    //跟上面差不多, 但是补刀的时候只能造成 1 伤害
    //cout << count1 <<" "<<count2<<" "<<count3<< endl;
    cout << ((count1<count2)?((count1<count3)?count1:count3):((count2<count3)?count2:count3)); //输出三个结果中最小的

    return 0;

}
// 64 位输出请用 printf("%lld")

编辑于 2023-07-15 14:17:56 回复(0)
题目设计属实不沾套路,这里可以分为两种情况去讨论:
1.最少次数打败的两只怪兽相邻
2.最少次数打败的两只怪兽不相邻
2.1 打败的怪兽在队列的第一个
2.2 打败的怪兽不在队列的第一个

第一种情况通过滑窗遍历,记录最小的打击次数
第二种情况,对原数组进行排序,分别讨论逐个击破的打击次数
最后,比较最小值即可得答案
#include <algorithm>
#include <climits>
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int main() {
    int n;
    cin>>n;
    int temp=n;
    vector<int> ret(n);
    for(auto& num:ret){
        cin>>num;
    }
    int minNum=ret[0]>ret[1]/2?ret[0]:(ret[1]+1)/2;
    int t1,t2;
    for(int i=0;i<n-2;i++){
        if(ret[i+1]>ret[i+2]/2){
            t2=(ret[i+2]+1)/2;
            t1=(ret[i+1]-t2+1)/2;
            minNum=min(t1+t2,minNum);
        }else{
            t2=(ret[i+2]+1)/2;
            minNum=min(t2,minNum);
        }
    }
    int first=ret[0];
    sort(ret.begin(),ret.end());
    if(ret[0]!=first && ret[1]!=first){
        minNum=min((ret[0]+1)/2+(ret[1]+1)/2,minNum);
    }else if(ret[0] == first){
        minNum=min(min(first+(ret[1]+1)/2,(ret[1]+1)/2+(ret[2]+1)/2),minNum);
    }else if(ret[1] == first){
        minNum=min(min(first+(ret[0]+1)/2,(ret[0]+1)/2+(ret[2]+1)/2),minNum);
    }
    cout<<minNum<<endl;

}


发表于 2023-03-29 17:49:09 回复(0)
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        int len = in.nextInt();
        int[] nums = new int[len];
        int min1 = Integer.MAX_VALUE;
        int min2 = Integer.MAX_VALUE;

        for (int i = 0; i < len; i++) {
            nums[i] = in.nextInt();
        }
        int res = Integer.MAX_VALUE;

        for (int i = 1; i < len; i++) {
            // 求两个最小值,不包含数组第一个元素
            if (nums[i] < min2) {
                if (nums[i] < min1) {
                    min2 = min1;
                    min1 = nums[i];
                } else {
                    min2 = nums[i];
                }
            }

            // 杀死相邻两个怪物,需要的最少次数
            // 杀死当前怪物 i
            int right = (nums[i] + 1)/ 2;
            // 杀死前一个怪物 i-1
            int left = nums[i - 1];
    
            if (right >= left) {
                res = Math.min(res, right);
            } else if (left > right) {
                if (i > 1) {
                    res = Math.min(res, right + ((left - right + 1) / 2));
                } else {
                    res = Math.min(res, left);
                }
            }
        }

        int tmp = 0;
        if (nums[0] < (min2 + 1) / 2) {
            tmp = (min1 + 1) / 2 + nums[0];
        } else {
            tmp = (min1 + 1) / 2 + (min2 + 1) / 2;
        }
        
        System.out.println(Math.min(tmp, res));
    }
}

发表于 2023-03-15 14:49:00 回复(1)