AtCoder Beginner Contest 175

コンテスト前のレート:1775
レート通りのパフォーマンスが得られる順位:528位

A - Rainy Season

問題
長さが3しかないので、愚直にif文を書く方針とした。

思考過程
  1. "RRR"、"RR"、"R"の順に、Sに含まれているか試す。
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        sc.close();

        if (s.indexOf("RRR") != -1) {
            System.out.println(3);
        } else if (s.indexOf("RR") != -1) {
            System.out.println(2);
        } else if (s.indexOf("R") != -1) {
            System.out.println(1);
        } else {
            System.out.println(0);
        }
    }
}

ここまで1分22秒+0ペナ。162位。


B - Making Triangle

問題
場合分けが極力少なくなるように考えた。

思考過程
  1. O(N^3)が間に合う制約なので、3重ループを回して条件を満たすものを数える。
  2. カウントするものを、i \neq j \neq kかつL_i \lt L_j \lt L_kであるものに限定すれば、あとは三角形の成立条件としてL_i + L_j \gt L_kだけ判定すればよくなる。

後から気が付いたが、i \neq j \neq kL_i \lt L_j \lt L_kならば必ず満たされるので、なくても通る。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] l = new int[n];
        for (int i = 0; i < n; i++) {
            l[i] = sc.nextInt();
        }
        sc.close();

        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    if (i != j && j != k
                            && l[i] < l[j] && l[j] < l[k]
                            && l[i] + l[j] > l[k]) {
                        ans++;
                    }
                }
            }
        }
        System.out.println(ans);
    }
}

ここまで5分05秒+0ペナ。336位。


C - Walking Takahashi

問題
K \times Dでオーバーフローしないようにすることだけは常に頭に置いていた。

思考過程
  1. 座標0からの距離だけがわかればいいので、Xの絶対値を取ってX \gt 0の場合のみ考える。
  2. K \times Dが大き過ぎればオーバーフローするが、十分小さければ計算する必要もあるので、場合分けしたい。
  3. KD \leq Xの場合、 X - KDが答え。ただし、オーバーフロー回避のため、移項してK \leq X/Dという判定にする。
  4. K \gt X/Dの場合、とりあえずまずは非負で0に最も近い座標X_2まで移動する。
  5. 残りの移動はX_2X_2-Dを行ったり来たりするので、残った移動回数R = K - X/Dの偶奇によりいずれかを出力する。
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        long x = sc.nextLong();
        long k = sc.nextLong();
        long d = sc.nextLong();
        sc.close();

        x = Math.abs(x);
        if (x / d >= k) {
            System.out.println(x - k * d);
        } else {
            long k2 = x / d;
            long x2 = x - k2 * d;
            long r = k - k2;
            r %= 2;
            x2 -= r * d; // 残りが偶数なら-0、奇数なら-D
            System.out.println(Math.abs(x2));
        }
    }
}

ここまで11分58秒+0ペナ。247位。
10分超えていてちょっと手間取ったと思ったけど十分早かったらしい。


D - Moving Piece

問題
初回WAの時点でE問題も見たが、すぐに解けそうになかったので基本的にはDを考えることにした。WAを重ねるにつれて徐々にEに浮気していくが、Dも完全には捨てずにいたらなんとか解けた。
最終的にコーナーケースが何だったかわかるまでに20分以上。

思考過程
  1. 問題の制約では、数字の6字型のような遷移はせず、必ず0字型の遷移になる。(途中からループになるのではなく、必ずスタート地点に戻る)
  2. O(N^2)が間に合うので、スタート地点N通りで毎回素直にスタートに戻るまでの遷移を行う。以下は1つのスタート地点についてのみ考える。
     
  3. スコアの合計値(one)と、合計値の最大値(max)を持ちながら、スタートに戻るまで遷移する。
  4. 戻る前にK回に達したら、その時点のmaxが答え。
  5. 1周したら、1周の合計値(one)が正でない場合、最初の1周の間のmaxが答え。
  6. oneが正の場合、最後の1周分手前まではone×周回分を計算してしまい、最後の1周分以上2周分未満の部分だけ、改めて合計値(one)と合計値の最大値(max2)を持ちながらシミュレーションを行う。
ペナルティ
  • 上記4で、全体のansとのmaxを取り忘れていた。 →9ケースWA
  • one×周回分を計算するところで、周回数をK/J-1ではなくK/Jとしていた(Jは周期)。確定で計算してしまってよいのは、ゴールから丸1周分以上手前まで。 →4ケースWA

本質的に解決していないとは思いながらも、ちょっとずつ書き換えながら4ケースWAを繰り返していた。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int[] p = new int[n];
        for (int i = 0; i < n; i++) {
            p[i] = sc.nextInt() - 1;
        }
        int[] c = new int[n];
        for (int i = 0; i < n; i++) {
            c[i] = sc.nextInt();
        }
        sc.close();

        long ans = Long.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            int idx = i;
            long max = Long.MIN_VALUE;
            long one = 0;
            for (int j = 1; j <= k; j++) {
                idx = p[idx];
                one += c[idx];
                max = Math.max(max, one);
                if (idx == i) { // 1周した場合
                    if (one > 0) { // 1周の合計が正の場合
                        long x = k / j;
                        long val = one * (x - 1); // 1周以上手前までの合計
                        long r = k % j + j; // 残りの移動回数
                        one = 0;
                        long max2 = 0;
                        for (int j2 = 0; j2 < r; j2++) {
                            idx = p[idx];
                            one += c[idx];
                            max2 = Math.max(max2, one);
                        }
                        val += max2;
                        max = Math.max(max, val);
                    }
                    break;
                }
            }
            ans = Math.max(ans, max);
        }
        System.out.println(ans);
    }
}

ここまで61分45秒+4ペナ。650位。
ここから最終結果まで思ったより転落して辛い。


E - Picking Goods

問題
4ケースTLEで解けず。
以下はTLE解法。

思考過程
  1. スタートを左上、ゴールを右下として、とりあえず上と左から大きい方を取って遷移するDPを考える。
  2. アイテムがないマスは、単純に上と左のmaxを取るのみ。
  3. アイテムがあるマスは、上と、左全部からの遷移を考える必要がある。
  4. もう少しよく考えると、左の遷移元はアイテムがあるマスのみ候補にすればよい。
  5. 遷移先マスから左に向かって、上位3個を保持しながら、「アイテムマスの1つ上+最大3個の合計」を求めていく。
  6. 遷移元として調べるマスがならしでO(K)なので、全体でO(KC)くらい?
    しかし、この見積もりが合ってたとしても、3個保持する分の3倍も入れて3 \times 3 \times 2 \times 10^8程度で10^9以上なのでやっぱり間に合ってなさそう。(0の数だけ見て10^8ならいけるのではと思ってしまった)
  7. 定数倍改善を試みるが当然変わらず。

 

E - Picking Goods(2020/8/16追記)

dp[x][y][z]:マス(x, y)まで見て、x行目でz個拾っている場合の最大値
で、3個の内訳とかも全然気にする必要もなく、ナップサックのようなDPをするだけだった。

  • 左からの遷移・・・拾わずにz個→z個の場合と、拾ってz-1個→z個の場合のmax。
  • 上からの遷移・・・拾わずに03個→0個の場合、拾って03個→1個の場合。

0個と1個の場合は、左と上のmaxも取る。

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] sa = br.readLine().split(" ");
        int h = Integer.parseInt(sa[0]);
        int w = Integer.parseInt(sa[1]);
        int k = Integer.parseInt(sa[2]);
        int[][] v = new int[h + 1][w + 1];
        for (int i = 0; i < k; i++) {
            sa = br.readLine().split(" ");
            int r = Integer.parseInt(sa[0]);
            int c = Integer.parseInt(sa[1]);
            v[r][c] = Integer.parseInt(sa[2]);
        }
        br.close();

        long[][][] dp = new long[h + 1][w + 1][4];
        for (int x = 1; x <= h; x++) {
            for (int y = 1; y <= w; y++) {
                // 左からの遷移
                for (int z = 1; z <= 3; z++) {
                    // max(拾わない、拾う)
                    dp[x][y][z] = Math.max(dp[x][y - 1][z], dp[x][y - 1][z - 1] + v[x][y]);
                }
                // 上からの遷移&左とのmax
                long ue = max(dp[x - 1][y]); // 1つ上のマス
                dp[x][y][0] = Math.max(dp[x][y - 1][0], ue); // max(左(拾わない)、上(拾わない))
                dp[x][y][1] = Math.max(dp[x][y][1], ue + v[x][y]); // max(左、上(拾う))
            }
        }
        System.out.println(max(dp[h][w]));
    }

    static long max(long[] a) {
        long ret = 0;
        for (int i = 0; i < a.length; i++) {
            ret = Math.max(ret, a[i]);
        }
        return ret;
    }
}

 

F - Making Palindrome

問題
Dが通ったところで一応5分ほど考えたが、正解者数の少なさも見て無理だと思ったので早々に諦め。
 


終結果:ABCDの4完、81分45秒、1109位、パフォーマンス1450
レート変動:1775→1747


5完最下位ならパフォ1730、Dが早く解けたら1500強、Dが解けなくても1300程度といったところだったので、Dの恩恵が少なく、Eが解ければセーフ、Eが早く解ければおいしいという感じだった。

青diffまでの問題が枯渇しているので、最近は黒diffから程よい問題を発掘するべく、JOIの難易度6~8埋めをしているが、今日もDPができなかったし、DPまとめコンテストとかをやった方がいいのかもしれない。