ardririyの足跡

AWC0001β D - 街道の商人

AWC0001β D - 街道の商人

なんか久々なせいか計算量解析がガバガバだったのでメモ。

問題概要

正整数M,KM,Kと長さNNの数列A,BA, Bが与えられます。(1,2,,N)(1, 2, \cdots, N)の連続するとは限らない部分列P=(P1,P2,,PP)P=(P_1, P_2, \cdots, P_{|P|})であって、以下の条件を満たす物のうちi=1PAPi\sum_{i=1}^{||P|} A_{P_i}の最大値を求めてください。

  • i=1PBPiM\sum_{i=1}^{||P|} B_{P_i} \leq M
  • i=1,2,,PP1i=1,2,\cdots, P_{|P|} -1 について、Pi+1PiKP_{i+1} - P_i \leq K

問題リンク

解法

どう見てもDPなのでDPテーブルを考える。MMが最大で200と小さいので、そこまでのBの和を持っておくのが自然。dpi,j:=\text{dp}_{i,j} := 条件を満たす列であって最後にiiを使い、BBの和がjjであるような列のうちAiA_iの和としてあり得る最大値、とするDPをすればよい。

空間はO(NM)O(NM)。遷移は、dpi,jdp_{i,j}へと遷移可能なのはk=1,2,,Kk=1,2,\cdots,Kについてdpik,jBi\text{dp}_{i-k,j-B_i}KK通りしか無いのでO(K)O(K)で良い。全体でO(NMK)O(NMK)で、余裕を持って通る。

提出(Rust, 7ms)

use library::utils::{chlibs::ChLibs, input::Input, iterlibs::collect::CollectIter};

fn solve(ip: &mut Input) {
    let (n, m) = ip.pair::<usize>();
    let k = ip.next::<usize>();
    let v = (0..n)
        .map(|_| (ip.next::<i64>(), ip.next::<usize>()))
        .collect_vec();

    let mut dp = vec![vec![0i64; m + 1]; n];

    for (i, &(a, b)) in v.iter().enumerate() {
        let mut mn = vec![0; m + 1];

        for j in if i < k { 0 } else { i - k }..i {
            for l in 0..=m {
                mn[l].chmax(dp[j][l]);
            }
        }

        for c in 0..m {
            if c + b <= m {
                dp[i][c + b].chmax(mn[c] + a);
            } else {
                break;
            }
        }
    }

    println!("{}", dp.iter().flatten().max().unwrap());
}

自省用にアホのメモをペタリ

IMG_0183.jpeg