第三场小白赛

A. 召唤神坤

先预处理出来每一个位置前后的最大值, 从前往后扫一下取max, 显然用 ( 前 m a x + 后 m a x ) a [ i ] \frac{(前max+后max)}{a[i]} a[i](max+max)对于当前位置来说其值是最大的.

import java.io.*;

public class A {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer stmInput = new StreamTokenizer(br);
    static int N = 100010, INF = 0x3f3f3f3f;
    static int a[] = new int[N], l[] = new int[N], r[] = new int[N];
    static int n, max;

    public static int readInt() throws IOException {
        stmInput.nextToken();
        return (int) stmInput.nval;
    }

    public static void solve() throws IOException{
        n = readInt();
        for(int i = 0; i < n; i++) a[i] = readInt();

        max = -INF;
        for(int i = 0; i < n; i++){
            l[i] = max;
            max = Math.max(max, a[i]);
        }
        max = -INF;
        for(int i = n - 1; i >= 0; i--){
            r[i] = max;
            max = Math.max(max, a[i]);
        }
        int res = 0;
        for(int i = 0; i < n; i++){
            res = Math.max(res, (l[i] + r[i]) / a[i]);
        }
        pw.println(res);
    }

    public static void main(String[] args) throws IOException {

        int T = 1;
        while(T-- != 0){
            solve();
        }

        pw.flush();
        pw.close();
        br.close();
    }

}

B. 聪明的交换策略

从集合的角度考虑问题, 最终结果只有两种方案:

  1. 1111…000000…
  2. 0000…111111…

对于任意情况比如 00101001001, 对照上述两种情况进行交换, 比如移动1.

如果是第一种情况1111…000000…, 对于00101001001, 从前往后扫, 遇到的第 i d x idx idx个1就应该在第 i d x idx idx位上, 那么把它从第i位移动到第 i d x idx idx位所需要的最小操作次数就是 i − i d x i-idx iidx, 累加即可.

第二种情况就是倒过来了.

import java.util.Scanner;

public class B {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String str = sc.next();
        int idx = 0;
        long ans1 = 0, ans2 = 0;
        for(int i = 0; i < n; i++){
            if (str.charAt(i) == '1'){
                ans1 += i - idx;
                idx++;
            }
        }
        idx = n - 1;
        for (int i = n - 1; i >= 0; i--){
            if (str.charAt(i) == '1'){
                ans2 += idx - i;
                idx--;
            }
        }
        System.out.println(Math.min(ans1, ans2));
        sc.close();
    }

}

这个题比较简单, 还有一类题型和这个长得比较像, 是给定三类值, 给定排好的序列, 问最少需要多少次交换, 感兴趣的同学可以看下, y总都有视频讲解:
整理书籍
交换座位
三值序列排序

C. 怪兽突击

如果要想打第i只怪兽, 那么得把它前i-1只怪兽都打过才行.

如果把前i只怪都打过, 很明显在前i只中选取 m i n ( a [ i ] + b [ i ] ) min(a[i]+b[i]) min(a[i]+b[i]), 剩下的次数都打这个怪.

由于前面的限制, 所以需要累加一下前面的sum, 同时每次取 m i n ( a [ i ] + b [ i ] ) min(a[i]+b[i]) min(a[i]+b[i])即可

import java.io.*;

public class C {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer stmInput = new StreamTokenizer(br);
    static int N = 100010;
    static int a[] = new int[N], b[] = new int[N];
    static int n, k;

    public static int readInt() throws IOException {
        stmInput.nextToken();
        return (int) stmInput.nval;
    }

    public static void solve() throws IOException{
        n = readInt();
        k = readInt();
        for (int i = 1; i <= n; i++) a[i] = readInt();
        for (int i = 1; i <= n; i++) b[i] = readInt();

        long sum = 0, min = (long) 1e18, ans = (long) 1e18;
       	// 前i天的怪都打过一次后, 自然是在这前i天中找最小的(a[i]+b[i]), 用剩下的k-i次去打
        for (int i = 1; i <= Math.min(n, k); i++){
            sum += a[i];
            min = Math.min(min, a[i] + b[i]);
            ans = Math.min(ans, sum + (k - i) * min);
        }
        pw.println(ans);
    }

    public static void main(String[] args) throws IOException {

        int T = 1;
        while(T-- != 0){
            solve();
        }

        pw.flush();
        pw.close();
        br.close();
    }

}

D. 蓝桥快打

如果小蓝先手要赢, 那么他要使得 他攻击小桥的次数<=小桥攻击他的次数, 假设小蓝的攻击力为d, 那么:

小蓝攻击小桥并且小蓝获胜所需的最小次数: x = ⌈ 小桥的体力 B 小蓝的攻击力 D ⌉ x=\lceil \frac{小桥的体力B}{小蓝的攻击力D} \rceil x=小蓝的攻击力D小桥的体力B

小桥攻击小蓝并且小桥获胜所需的最小次数: y = ⌈ 小蓝的体力 A 小桥的攻击力 C ⌉ y=\lceil \frac{小蓝的体力A}{小桥的攻击力C} \rceil y=小桥的攻击力C小蓝的体力A

很明显, 只要找到最小的d, 使得 x < = y x<=y x<=y, 二分答案即可

import java.io.*;

public class D {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer stmInput = new StreamTokenizer(br);

    static int n, m;

    public static int readInt() throws IOException{
        stmInput.nextToken();
        return (int) stmInput.nval;
    }

    public static void solve() throws IOException {
        double a = readInt(), b = readInt(), c = readInt();
        int l = 1, r = (int) 1e9 + 10;
        while (l < r){
            int mid = l + r >> 1;
            if (Math.ceil(b / mid) <= Math.ceil(a / c)) r = mid;
            else l = mid + 1;
        }
        pw.println(r);
    }

    public static void main(String[] args) throws IOException {

        int T = readInt();
        while(T-- != 0){
            solve();
        }

        pw.flush();
        pw.close();
        br.close();
    }

}

关于二分, 可以看下我下面的总结, 感觉应该对大家有所帮助:

// 二分模板一共有两个,分别适用于不同情况。
// 算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。
// 模板1(check左边)(取右边界)
// 当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。
public static void f1(int l,int r){
    while(l<r){
        int mid = l + r + 1 >> 1;
        if(check(mid)){
            l = mid;
        }else{
            r = mid - 1;
        }
    }
    return l;
}
// 对于模板1, 因为是check左边, 取右边界, 所以mid及其左边的所有值都满足条件.
// 一般这种是题目要求取"最大的最小值", "可能取小了"和"一定取大了"
// 1.如果check满足条件,l=mid, 说明"可能取小了"; 
// "可能" 是指(此时的mid是满足条件的, 有可能存在比它更大的数也满足要求), "取小了"是指(可能存在比此时mid更大的值也满足check要求, 这时还会继续二分往mid右边去查)
// 2反之, check不满足条件,r=mid-1, 说明"一定取大了"
// "一定"是指(此时的mid一定是不满足要求的), "取大了"是指(此时mid不满足要求, 要从mid-1往左去找)


// 模板2(check右边)(取左边界)
// 当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。
public static void f2(int l,int r){
    while(l<r){
        int mid = l + r >> 1;
        if(check(mid)){
            r = mid;
        }else{
            l = mid + 1;
        }
    }
    return l;
}
// 同理, 一般这里要求取"最小的最大值", "可能取大了"和"一定取小了", 请自行分析

E. 奇怪的段

动态规划, 线性dp

f [ i ] [ j ] f[i][j] f[i][j]表示将前i个数划分成j段的所有方案的集合.

  1. 对于第i个元素, 它属于上一段. f [ i − 1 ] [ j ] + a [ i ] ∗ p [ j ] f[i-1][j]+a[i]*p[j] f[i1][j]+a[i]p[j]
  2. 对于第i个元素, 从它开始新开一段. f [ i − 1 ] [ j − 1 ] + a [ i ] ∗ p [ j ] f[i-1][j-1]+a[i]*p[j] f[i1][j1]+a[i]p[j]

二类取max即可

由于有负数, 需要进行初始化f

根据定义初始化 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0

在做这个题之前, 最好是做过: 最大连续子序列

import java.io.*;
import java.util.*;

public class E {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer stmInput = new StreamTokenizer(br);
    static int N = 100010, M = 210;
    static int a[] = new int[N], p[] = new int[M];
    static long f[][] = new long[N][M], INF = (long) 1e18;
    static int n, m;

    public static int readInt() throws IOException{
        stmInput.nextToken();
        return (int) stmInput.nval;
    }

    public static void solve() throws IOException {
        n = readInt();
        m = readInt();
        for(int i = 1; i <= n; i++) a[i] = readInt();
        for(int i = 1; i <= m; i++) p[i] = readInt();
        for(int i = 0; i < N; i++) Arrays.fill(f[i], -INF);
        f[0][0] = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++){
                f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - 1]) + (long) a[i] * p[j];
            }
        }
        pw.println(f[n][m]);
    }

    public static void main(String[] args) throws IOException {

        int T = 1;
        while(T-- != 0){
            solve();
        }

        pw.flush();
        pw.close();
        br.close();
    }

}

F. 小蓝的反击

分解质因数+双指针

可以看: 【比赛题目讲解】蓝桥 · 算法双周赛 - 小白入门赛【第三场】_哔哩哔哩_bilibili

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值