Codeforces-Round 935 (Div. 3)A-E题解

os: 打完跑去睡觉,醒来后研究E题发现有更妙的写法,太妙了

A. Setting up Camp

题目:
给三种人的数量,内向人只能单独一个帐篷,外向人必须凑满三个人一个帐篷,普遍人随意安排,求最小的帐篷数量来满足以上所有人的需求

思路:
需要满足每个人的需求,外向人如果凑不齐三个就只能从普遍人中去凑,如果凑不齐就是没有可满足的方案;

package cf.contest.R935;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class A {

    private static InputStream is = System.in;
    private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
    private static final PrintWriter out = new PrintWriter(System.out);

    static void solve() throws IOException {
        String[] ins = in.readLine().split(" ");
        int a = Integer.parseInt(ins[0]),  b = Integer.parseInt(ins[1]), c = Integer.parseInt(ins[2]);
        int ans = 0;
        ans += a; // a单独占一个帐篷
        ans += b / 3; // b 凑满三个人
        int bl = b % 3; // b 剩余的人需要和c凑满三个人
        if (bl != 0 && bl + c < 3) { // 如果凑不满
            out.println(-1);
            return;
        } else {
            if (bl > 0) { // b和c凑满三个人
                ans ++;
                c -= 3 - bl;
            }
            // 给c分配帐篷,凑满三个人 c / 3的上取整
            ans += (c + 3 - 1) / 3;
        }
        out.println(ans);
    }

    public static void main(String[] args) throws IOException {
        int t = 1;
        t = Integer.parseInt(in.readLine());
        while (t -- > 0) {
            solve();
        }
        in.close();
        out.flush();
        out.close();
    }
}

B. Fireworks

题目:
有两个烟花发射器,第一个隔a分钟发射一次,第二个隔b分钟发射一次,每次发射的烟花可以持续 m + 1分钟,求同一时刻能够看到的最大烟数量

思路:
假设从0时刻同时发射即可两个烟花发射器同时发射,那答案就是在 m 分钟之内发射的烟花数量总数;a和b必定存在一个最小公倍数(必定存在某一个时刻会同时发射)

package cf.contest.R935;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class B {

    private static InputStream is = System.in;
    private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
    private static final PrintWriter out = new PrintWriter(System.out);

    static void solve() throws IOException {
        String[] ins = pStringArray();
        long a = pLong(ins[0]), b = pLong(ins[1]), m = pLong(ins[2]);
        out.println(2 + m / a + m / b); // 2 是 0 时刻同时发射的烟花数量, m / a 是在第一个烟花发射后的m间隔内发射的烟花数量
    }

    public static void main(String[] args) throws IOException {
        int t = 1;
        t = Integer.parseInt(in.readLine());
        while (t -- > 0) {
            solve();
        }
        in.close();
        out.flush();
        out.close();
    }

    private static int pInt(String s) {
        return Integer.parseInt(s);
    }
    private static long pLong(String s) {
        return Long.parseLong(s);
    }
    private static String[] pStringArray() {
        try {
            return in.readLine().split(" ");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}


C. Left and Right Houses

题目:
在村庄的一行房子之间造一条路,这条路将村庄分为左右两部分,每个房子的居民想分到左边 a i = 0 a_i=0 ai=0 或者右边 b i = 1 b_i=1 bi=1,要求每一部分需要满足该部分的有一半人以上(二分之一的上取整)是想分到该部分的,同时这条路应该靠近村庄的中心,输出路后面的房子的下标。

思路:
遍历,记录满足条件的答案即可(看代码注释)

package cf.contest.R935;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class C {

    private static InputStream is = System.in;
    private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
    private static final PrintWriter out = new PrintWriter(System.out);

    static void solve() throws IOException {
        int n = pInt(in.readLine());
        String s = in.readLine();
        int a = 0;
        // 先统计0和1的个数
        for (int i = 0; i < n; i ++) {
            if (s.charAt(i) == '0') {
                a ++;
            }
        }
        int b = n - a;
        // cnt 为从0到i的0的个数
        int cnt = 0, ans = 0;
        // min 是当前ans距离村中心的距离
        double min = Double.MAX_VALUE;

        for (int i = 0; i <= n; i ++) {
            int left = i, right = n - i;
            // 两部分的居民有二分之一以上的村民想分到对应的部分
            if ((left + 1) / 2 <= cnt && (right + 1) / 2 <= b - (i - cnt)) {
                if (Math.abs(n / 2.0 - i) < min) {
                    ans = i;
                    min = Math.abs(n / 2.0 - i);
                }
            }
            if (i < n && s.charAt(i) == '0') cnt ++;
        }
        out.println(ans);
    }

    public static void main(String[] args) throws IOException {
        int t = 1;
        t = Integer.parseInt(in.readLine());
        while (t -- > 0) {
            solve();
        }
        in.close();
        out.flush();
        out.close();
    }

    private static int pInt(String s) {
        return Integer.parseInt(s);
    }
    private static long pLong(String s) {
        return Long.parseLong(s);
    }
    private static String[] pStringArray() throws IOException {
        return in.readLine().split(" ");
    }
}


D. Seraphim the Owl

题目:
一队人排队,每个人都有对应的 a i a_i ai 值和 b i b_i bi 值;现在A处于最后面,他想到达前 m m m 个人的位置,每次他(当前位置 k k k)可以选择一个在他前面(位置 i i i) 的人交换位置,付给他 a i a_i ai个硬币,同时给位置 i i i k k k 之间的人( i < j < k i \lt j \lt k i<j<k)全部付给 b j b_j bj个硬币,求A到达前 m m m 个人所需要最小所需要的硬币数;

思路:
假设到达前m个人时最后的位置为x,如果直接交换(不和其他的人交换)那么需要付出的硬币数为 i ∈ ( x , n ] i \in (x, n] i(x,n] 区间的 b i b_i bi 值加上 a x a_x ax;此时最优解就是从 [ 1 , m ] [1, m] [1,m] 最小值,用维护 b 的前缀和 sum 求出 s u m [ n ] − s u m [ x ] + a [ x ] sum[n] - sum[x] + a[x] sum[n]sum[x]+a[x] 的最小值 c o s t cost cost;
如果从需要在 [ m + 1 , n ] [m + 1, n] [m+1,n] 区间内选交换的人,如何使得 c o s t cost cost 更小呢?
假设选取的人为位置 y y y,那么最终答案为 c o s t − b [ y ] + a [ y ] = c o s t + a [ y ] − b [ y ] cost - b[y] + a[y] = cost + a[y] - b[y] costb[y]+a[y]=cost+a[y]b[y],因为 c o s t cost cost 值是固定的,那么要找 a [ y ] − b [ y ] < 0 a[y] - b[y] < 0 a[y]b[y]<0 才能使得 c o s t cost cost 变小,因此遍历 y ∈ [ m + 1 , n ] y \in [m+1,n] y[m+1,n] 区间找到 a [ y ] − b [ y ] < 0 a[y] - b[y] < 0 a[y]b[y]<0 加上即可

package cf.contest.R935;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class D {

    static void solve() throws IOException {
        String[] ins = pStringArray();
        int n = pInt(ins[0]), m = pInt(ins[1]);
        int[] a = pIntArray(1), b = pIntArray(1);
        long[] sum = new long[n + 1];
        // 前缀和
        for (int i = 1; i <= n; i ++) {
            sum[i] = sum[i - 1] + b[i];
        }
        // 找到 [1, m] 区间内的最小值
        long preMin = Long.MAX_VALUE;
        int pos = -1;
        for (int i = 1; i <= m; i ++) {
            if (sum[n] - sum[i] + a[i] < preMin) {
                preMin = sum[n] - sum[i] + a[i];
                pos = i;
            }
        }

        long ans = sum[n] - sum[pos] + a[pos];
        // 找 [m + 1, n] 区间内 a[i] - b[i] < 0 的
        for (int i = m + 1; i <= n; i ++) {
            int dis = a[i] - b[i];
            if (dis <= 0) {
                ans += dis;
            }
        }
        out.println(ans);
    }


    public static void main(String[] args) throws IOException {
        int t = 1;
        t = Integer.parseInt(in.readLine());
        while (t -- > 0) {
            solve();
        }
        in.close();
        out.flush();
        out.close();
    }

    private static InputStream is = System.in;
    private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
    private static final PrintWriter out = new PrintWriter(System.out);
    private static int pInt(String s) {
        return Integer.parseInt(s);
    }
    private static long pLong(String s) {
        return Long.parseLong(s);
    }
    private static String[] pStringArray() throws IOException {
        return in.readLine().split(" ");
    }
    private static int[] pIntArray(int start) throws IOException {
        String[] s = pStringArray();
        int[] arr = new int[start + s.length];
        for (int i = start, j = 0; i < arr.length; i++, j ++) {
            arr[i] = Integer.parseInt(s[j]);
        }
        return arr;
    }
}

E. Binary Search

题目:
给一个n长度的排列p,需要找到x,至多交换两次排列p中的元素,使得二分查找能够查找到x;输出交换的元素对

思路:
假设二分查找最后的数为 y,那么必定满足 y < x,存在两种情况:

  • 二分查找的路径没有经过 mid,那么交换 xy 并不会影响判断,交换后最后一次判断 p[mid] = x <= x 的结果肯定不变
  • 二分查找的路径经过 mid,那么交换前 x 所处的位置肯定满足 x = p[mid] <= x,交换之后变成 y = p[mid] <=x 结果并不会变化

因此只需要执行一次二分查找,然后直接将 x 和二分查找到的位置进行交换即可

package cf.contest.R935;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class E {
    // 题目要求的二分查找
    private static int binarySearch(int[] p, int x) {
        int l = 1, r = p.length;
        while (r - l > 1) {
            int mid = l + r >> 1;
            if (p[mid] <= x) {
                l = mid;
            } else {
                r = mid;
            }
        }
        return l;
    }
    // 数组的交换元素
    private static void swap(int[] p, int i, int j) {
        int tmp = p[i];
        p[i] = p[j];;
        p[j] = tmp;
    }

    static void solve() throws IOException {
        String[] ins = pStringArray();
        int n = pInt(ins[0]), x = pInt(ins[1]);
        int[] p = pIntArray(1);
        // 找到 x 当前的位置
        int pos = -1;
        for (int i = 1; i <= n; i ++) {
            if (x == p[i]) {
                pos = i;
                break;
            }
        }
        int idx = binarySearch(p, x);
        out.println(1);
        out.println(idx + " " + pos);
    }
    
    public static void main(String[] args) throws IOException {
        int t = 1;
        t = Integer.parseInt(in.readLine());
        while (t -- > 0) {
            solve();
        }
        in.close();
        out.flush();
        out.close();
    }

    private static InputStream is = System.in;
    static {
        try {
            is = Files.newInputStream(Paths.get("F:\\Notes\\Algorithm\\Problems\\java\\java\\src\\main\\java\\input.txt"));
        } catch (Exception e) {
            is = System.in;
        }
    }
    private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
    private static final PrintWriter out = new PrintWriter(System.out);
    private static int pInt(String s) {
        return Integer.parseInt(s);
    }
    private static long pLong(String s) {
        return Long.parseLong(s);
    }
    private static String[] pStringArray() throws IOException {
        return in.readLine().split(" ");
    }
    private static int[] pIntArray(int start) throws IOException {
        String[] s = pStringArray();
        int[] arr = new int[start + s.length];
        for (int i = start, j = 0; i < arr.length; i++, j ++) {
            arr[i] = Integer.parseInt(s[j]);
        }
        return arr;
    }
}


  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值