1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Advent of Code

毎日2題出題されます。競技プログラミングチックに楽しめます。

この問題概要と実装解答を以下にご紹介します。
問題概要は生成AIに書いてもらったものを貼り付けています。
実装は人間が行なっています。

Day 10: Factory

part1 問題概要

インジケータランプの目標パターン:[.##.] のような角括弧 と ボタンの配線情報:(0,3,4) のような丸括弧 () が複数与えられます。

初期状態はすべてオフ (.....) からスタートして、ボタンを押して入力で与えられたパターンと一致させるのが目標。

ボタンiを押すと括弧内の箇所が., #でトグルする。

目標の文字列になるまでの最小ボタン押下回数を教えてください。

解答

括弧の文字列が多くて10個くらいなので、すべての状態は <= 2 ^ 10 = 1024くらいで余裕で全状態を保持できます。
よって、BFSが可能です。

use std::{collections::{HashMap, VecDeque}, io::{self, Read}};

use itertools::Itertools;
use regex::Regex;

fn parse_input(s: &str) -> (Vec<char>, Vec<Vec<usize>>, Vec<usize>) {
    let reg1 = Regex::new(r"\[([.#]+)\]").unwrap();
    let reg2 = Regex::new(r"\(([^)]+)\)").unwrap();
    let reg3 = Regex::new(r"\{([^}]+)\}").unwrap();
    let mut v = vec![];
    for p in reg2.captures_iter(&s) {
        let t = &p[1];
        let nums = t.split(',').map(|e| e.parse::<usize>().unwrap()).collect_vec();
        v.push(nums);
    }
    let s1 = reg1.captures(&s).unwrap()[1].chars().collect_vec();
    let s3 = reg3.captures(&s).unwrap()[1].split(',').map(|e| e.parse::<usize>().unwrap()).collect_vec();
    
    (s1, v, s3)
}

fn main() {
    let mut input = String::new();
    io::stdin().read_to_string(&mut input).unwrap();
    let s = input
        .lines().collect_vec();
    let t = s.iter().map(|e| parse_input(e)).collect_vec();

    let mut ans = 0;
    for (target, button, _) in t.iter() {
        let n = target.len();
        let mut dist = HashMap::new();
        dist.insert(vec!['.'; n], 0usize);
        let mut que = VecDeque::new();
        que.push_back(vec!['.'; n]);
        while !que.is_empty() {
            let v = que.pop_front().unwrap();
            let &now = dist.get(&v).unwrap();
            for row in button.iter() {
                let mut new = v.to_owned();
                for &idx in row.iter() {
                    new[idx] = if new[idx] == '#' {'.'} else {'#'};
                }
                let pre = dist.get(&new);
                if pre.is_some() {
                    continue;
                }
                dist.insert(new.to_owned(), now + 1);
                que.push_back(new);
            }
        }
        let &a = dist.get(target).unwrap();
        ans += a;
    }
    println!("{}", ans);
}

part2問題概要

中括弧で数字が

(3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}

のように与えられます。
先ほどと同様にボタンを押すことで書かれたindexがインクリメントされます。

数字が中括弧のようになるようなボタンの押下最小回数を教えて!

解答

これ入力見ると中括弧の数字は最大300くらいあって、要素数も10程度あるので、全状態は 300 ^ 10 くらいになるのでとんでもない回数になり、先ほどと同様なbfsすると一生終わらないと思います。

そこで、例えば↑の例では、0番の3という数字は5番目と6番目のボタンを使ってやる必要があるので、
⑤ + ⑥ = 3
という式が成立します。

そのほかの番号に対してもそれぞれ書けるので、結局行列Aに対して

$$
Ax = B
$$

を満たすxについて解く問題に帰着できます。

詳しいところは
https://drken1215.hatenablog.com/entry/2019/03/20/202800
を見ていただければと思いますが、連立方程式はガウスの吐き出し法でO(N^3)で解けます。

ただ、今回は自由度が出てくるので、連立方程式をガウスの吐き出し法でといておいて、ピポッドとランクを見つけて、自由度を算出します。

その後自由度を持った変数について枝刈り全探索をdfsで行って答えを算出します。

難しかった....

use std::{
    cmp::min,
    io::{self, Read},
};

use itertools::Itertools;
use num::integer::gcd;
use regex::Regex;

fn parse_input(s: &str) -> (Vec<char>, Vec<Vec<usize>>, Vec<isize>) {
    let reg1 = Regex::new(r"\[([.#]+)\]").unwrap();
    let reg2 = Regex::new(r"\(([^)]+)\)").unwrap();
    let reg3 = Regex::new(r"\{([^}]+)\}").unwrap();
    let mut v = vec![];
    for p in reg2.captures_iter(&s) {
        let t = &p[1];
        let nums = t
            .split(',')
            .map(|e| e.parse::<usize>().unwrap())
            .collect_vec();
        v.push(nums);
    }
    let s1 = reg1.captures(&s).unwrap()[1].chars().collect_vec();
    let s3 = reg3.captures(&s).unwrap()[1]
        .split(',')
        .map(|e| e.parse::<isize>().unwrap())
        .collect_vec();

    (s1, v, s3)
}

fn gauss(v: &mut Vec<Vec<isize>>) -> (usize, Vec<usize>) {
    let n = v.len();
    let m = v[0].len() - 1;
    let mut rank = 0;
    let mut cs = vec![];
    for col in 0..m {
        let mut t = usize::MAX;
        for row in rank..n {
            if v[row][col] != 0 {
                t = row;
                break;
            }
        }
        if t == usize::MAX {
            continue;
        }
        v.swap(rank, t);
        cs.push(col);
        let p = v[rank][col];
        for row in (rank + 1)..n {
            if v[row][col] == 0 {
                continue;
            }
            let g = gcd(p, v[row][col]);
            let t = v[row][col] / g;
            let tt = p / g;
            for j in col..=m {
                v[row][j] = v[row][j] * tt - v[rank][j] * t;
            }
        }
        rank += 1usize;
    }
    (rank, cs)
}

fn dfs(
    idx: usize,
    now: &mut Vec<isize>,
    nowsum: isize,
    table: &Vec<Vec<isize>>,
    v: &Vec<usize>,
    rv: &Vec<usize>,
    ans: &mut isize,
    lim: isize,
) {
    if nowsum >= *ans {
        return;
    }
    let m = table[0].len() - 1;
    let mm = rv.len();
    if idx == v.len() {
        for rank in (0..mm).rev() {
            let col = rv[rank];
            let mut right = table[rank][m];
            for j in (col + 1)..m {
                right -= table[rank][j] * now[j];
            }
            let tm = table[rank][col];

            if tm == 0 {
                if right != 0 {
                    return;
                } else {
                    continue;
                }
            }

            if right % tm != 0 {
                return;
            }
            let val = right / tm;
            if val < 0 {
                return;
            }
            now[col] = val;
        }
        let t = now.iter().sum::<isize>();
        *ans = min(*ans, t);
        return;
    }
    for c in 0..=lim {
        now[v[idx]] = c;
        dfs(idx + 1, now, nowsum + c, table, v, rv, ans, lim);
    }
}

fn solve(button: Vec<Vec<usize>>, target: Vec<isize>) -> isize {
    let m = button.len();
    let n = button.iter().flatten().max().unwrap() + 1;

    let mut table = vec![vec![0; m + 1]; n];
    for i in 0..m {
        for &j in button[i].iter() {
            table[j][i] = 1isize;
        }
    }
    for i in 0..n {
        table[i][m] = target[i];
    }

    let (_, cs) = gauss(&mut table);
    let t = (0..m).filter(|&e| !cs.contains(&e)).collect_vec();
    let rt = (0..m).filter(|&e| cs.contains(&e)).collect_vec();
    let lim = target.iter().sum::<isize>();
    let mut ans = isize::MAX;
    let mut now = vec![0; m];
    dfs(0, &mut now, 0, &table, &t, &rt, &mut ans, lim);
    ans
}

fn main() {
    let mut input = String::new();
    io::stdin().read_to_string(&mut input).unwrap();
    let s = input.lines().collect_vec();
    let t = s.iter().map(|e| parse_input(e)).collect_vec();

    let mut ans = 0;
    for (_, button, target) in t {
        ans += solve(button, target);
    }
    println!("{}", ans);
}

終わりに

part2がこれまでの問題で一番難しかったです。
day11も頑張ります!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?