AtCoder Beginner Contest 328)题解 A-F

目录

  • A - Not Too Hard
  • B - 11/11
  • C - Consecutive
  • D - Take ABC
  • E - Modulo MST
  • F - Good Set Query

A - Not Too Hard

原题链接

题目描述
给出 N 个数字,找出小于等于 X 的数字之和。

public static void solve() throws IOException{
    int n = readInt(), x = readInt();
    long s = 0;
    for (int i = 0; i < n; i++) {
        int p = readInt();
        if (p <= x) s += p;
    }
    printWriter.println(s);
}

B - 11/11

原题链接

题目描述
有一张一年有N个月的日历,每个月有 D i D_i Di 天,如果第i个月的第j天,两个数字的每一位都相同,那么当天与当月是一个重数字日期。如第1个月的第1天和第11天,两天都属于重数字日期,问N个月中有多少个重数字日期。

思路:暴力枚举

  • 暴力检查每个月的每一天是否为重数字日期。
public static void solve() throws IOException {
    int n = readInt();
    int[] days = new int[n];
    for (int i = 0; i < n; i++) {
        days[i] = readInt();
    }
    int sum = 0;
    for (int i = 1; i <= n; i++) {// 枚举月
        for (int j = 1; j <= days[i - 1]; j++) {// 枚举日
            String s1 = String.valueOf(i), s2 = String.valueOf(j);
            boolean f = true;
            for (int a = 0; a < s1.length(); a++) {
                for (int b = 0; b < s2.length(); b++) {
                    if (s1.charAt(a) != s2.charAt(b)) {
                        f = false;
                        break;
                    }
                }
                if (!f) break;
            }
            if (f) sum++;
        }
    }
    printWriter.println(sum);
}

C - Consecutive

原题链接

题目描述
给定一个长度为N的由小写字母组成的字符串,再给出Q个查询,每次查询由两个整数 [ l , r ] [l, r] [l,r] 组成,对于每次询问,查询 S l , S l + 1 , . . . S r S_l,S_{l+1},...S_r Sl,Sl+1,...Sr 中总共有多少个连续的两个相同的小写字母。

思路:动态规划

  • dp数组的含义为以第 i i i 个字符结尾,从 1 ∼ i 1 \sim i 1i 中总共有多少个连续的两个相同的小写字母。
public static void solve() throws IOException {
    int n = readInt(), q = readInt();
    String s = (" " + readString());
    int[] dp = new int[n + 10];
    dp[1] = 0;
    for (int i = 2; i <= n; i++) {
        if (s.charAt(i) == s.charAt(i - 1)) {
            dp[i] = dp[i - 1] + 1;
        } else {
            dp[i] = dp[i - 1];
        }
    }
    while (q-- > 0) {
        int l = readInt(), r = readInt();
        printWriter.println(dp[r] - dp[l]);
    }
}

D - Take ABC

原题链接

题目描述
给定一个由ABC组成的字符串S,只要S中包含子串ABC,就要进行以下的操作:从左边开始删除子串ABC,删除后,剩下的子串进行拼接,重复执行以上的操作。输出最终的字符串。

思路:栈模拟

  • 从左到右,字符入栈时,先进行以下操作:判断栈顶字符是否为C,如果为C,那么弹出栈顶的三个字符,判断三个字符是否为子串ABC,如果是,那么继续执行上面的操作;如果不是,则将刚刚弹出的元素重新入栈,但要注意入栈顺序。
public static void solve() throws IOException {
    String s = readString();
    Deque<Character> deque = new LinkedList<>();
    for (int i = 0; i < s.length(); i++) {
        deque.push(s.charAt(i));
        while (deque.size() >= 3 && deque.peek() == 'C') {
            char ch1 = deque.pop(), ch2 = deque.pop(), ch3 = deque.pop();
            if (ch1 != 'C' || ch2 != 'B' || ch3 != 'A') {
                deque.push(ch3);
                deque.push(ch2);
                deque.push(ch1);
                break;
            }
        }
    }
    StringBuilder sb = new StringBuilder();
    while (deque.size() > 0) {
        sb.append(deque.pop());
    }
    printWriter.println(sb.reverse().toString());
}

E - Modulo MST

原题链接

题目描述
给定N个顶点、M条无向边和一个模数K,每条边由一个三元组 ( u , v , w ) (u,v,w) (u,v,w) 组成,表示顶点u和顶点v的权重为w,请根据这M条边构成最小生成树T,代价为各边权重之和,并对K取模,请求出构成T的最小成本。

思路:并查集+dfs

  • 该题无法通过最小生成树算法实现,因为每次累加权重和时,要对K进行取模,比如点 1 → 2 1 \to 2 12的权重为 6 6 6 2 → 3 2 \to 3 23权重为 3 3 3 2 → 4 2 \to 4 24权重为 4 4 4 3 → 4 3 \to 4 34权重为 1 1 1,模数为 11 11 11, 如果使用最小生成树算法得最小代价为 10 10 10,实际结果为 0 0 0
  • 由于边数较少,我们可以爆搜,从M条边中选出N-1条边,再通过并查集判断N个点是否处于同一联通块,形成生成树。枚举每一种方案,时间复杂度最坏是 O 28 7 O_{28}^{7} O287
static int n, m;
static int[] p;
static long mod, res = Long.MAX_VALUE;
static List<Edge> edges;
static boolean[] st;

public static void solve() throws IOException {
    n = readInt(); m = readInt();
    mod = readLong();
    edges = new ArrayList<>();
    st = new boolean[m + 1];
    for (int i = 0; i < m; i++) {
        edges.add(new Edge(readInt(), readInt(), readLong()));
    }
    dfs(0, 0);

    printWriter.println(res);
}

// cnt表示当前选择了多少条边,cur表示当前选中的最后一条边
public static void dfs(int cnt, int cur) {
    if (cnt > n - 1) return;
    // n个点,n-1条边
    if (cnt == n - 1) {
        p = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            p[i] = i;
        }
        long curSum = 0;
        for (int i = 0; i < m; i++) {
            if (st[i]) {// 这条边被选中了
                int u = edges.get(i).pe, v = edges.get(i).to;
                long w = edges.get(i).w;
                int pu = find(u), pv = find(v);
                p[pu] = pv;// 合并
                curSum = (curSum + w) % mod;
            }
        }
        boolean ok = true;
        int p1 = find(1);
        // 判断 n个点是否在一个联通块
        for (int i = 1; i <= n; i++) {
            if (find(i) != p1) {
                ok = false;
                return;
            }
        }
        if (ok) {
            res = Math.min(res, curSum);
        }
        return;
    }
    for (int i = cur; i < m; i++) {
        st[i] = true;
        dfs(cnt + 1, i + 1);
        st[i] = false;
    }
}

public static int find (int x) {
    if (x != p[x]) {
        p[x] = find(p[x]);
    }
    return p[x];
}

F - Good Set Query

原题链接

题目描述
给定一个整数NQ个三元组 ( a , b , d ) (a,b,d) (a,b,d),并且初始化一个空集合S,从头开始遍历Q个三元组,如果存在一个整数序列 ( X 1 , X 2 , . . . X N ) (X_1,X_2,...X_N) (X1,X2,...XN),使得集合S中的所有三元组都满足 X a i − X b i = d i X_{a_i} - X_{b_i} = d_i XaiXbi=di,对于所有的 i ∈ S i \in S iS,那么当前三元组会被添加至集合S,按升序数组最终集合S中所有三元组的编号。

思路:带权并查集

  • 遍历每一个三元组时,判断ab是否已经在同一联通块中,如果在,判断差值是否冲突;如果不在,那么一定可以构造出 X a − X b = d X_a - X_b = d XaXb=d
static int[] p;
static long[] val;

public static void solve() throws IOException{
    int n = readInt(), q = readInt();
    p = new int[n + 1];
    val = new long[n + 1];
    for (int i = 1; i <= n; i++) p[i] = i;

    for (int i = 1; i <= q; i++) {
        int a = readInt(), b = readInt(), d = readInt();
        int pa = find(a), pb = find(b);
        if (pa == pb) {
            // 已经在一个联通块中,判断差值是否相等
            if (val[a] - val[b] == d) {
                printWriter.print(i + " ");
            }
        } else {
            // 没在一个联通块中,那么当前三元组一定可以放入集合 S,并更新差值
            p[pa] = pb;
            val[pa] = d + val[b] - val[a];
            printWriter.print(i + " ");
        }
    }
}

public static int find(int x) {
    if (x != p[x]) {
        int t = p[x];
        p[x] = find(p[x]);
        val[x] += val[t];
    }
    return p[x];
}

你可能感兴趣的:(并查集,动态规划dp,深度优先搜索dfs,算法)