use std::time::{Instant, Duration};
use rand::{Rng, SeedableRng};
use rand::rngs::SmallRng;
use proconio::input;
use proconio::marker::Usize1;

struct Input {
    d: i32,
    c: Vec<i32>,
    s: Vec<Vec<i32>>
}

#[proconio::fastout]
fn main() {
    let start = Instant::now();
    input! {
        d: i32,
        c: [i32; 26],
        s: [[i32; 26]; d],
    }
    let input = Input{d, c, s};
    let mut state = State::from_greedy(&input);
    println!("{}", state);
    eprintln!("score: {}({})", std::cmp::max(10i32.pow(6)+state.score, 0), state.score - comute_score(&input, &state.schedule));
}
fn comute_score(input: &Input, schedule: &Vec<usize>) -> i32 {
    let mut score = 0;
    let mut last = vec![0; 26];
    for d in 0..schedule.len() {
        last[schedule[d]] = d + 1;
        for i in 0..26 {
            score -= (d + 1 - last[i]) as i32 * input.c[i];
        }
        score += input.s[d][schedule[d]];
    }
    score
}

struct State {
    score: i32,
    schedule : Vec<usize>,
    ds: Vec<Vec<usize>>
}

impl std::fmt::Display for State {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
       let s: String = self.schedule.iter()
                       .map(|x| (x+1).to_string())
                       .collect::<Vec<String>>()
                       .join("\n");
        write!(f, "{}", s)
    }
}

impl State {
    fn new(input: &Input, schedule: Vec<usize>) -> State {
        let mut ds: Vec<Vec<usize>> = vec![vec![]; 26];
        for d in 0..input.d {
            ds[schedule[d as usize]].push(d as usize+1);
        }
        let score = comute_score(&input, &schedule);
        State {score, schedule, ds}
    }
    fn from_greedy(input: &Input) -> State {
        let mut score = 0;
        let mut ds: Vec<Vec<usize>> = vec![vec![]; 26];
        let mut schedule = vec![0; input.d as usize];
        let mut last = vec![0; 26];
        for i in 0..input.d {
            let mut best_contest: usize = 0;
            let mut best_satification = -1_000_000_000;
            for t in 0..26 {
                let pre_last = last[t];
                last[t] = i;
                let tmp_satification = {
                    score
                    + input.s[i as usize][t as usize]
                    - last.iter().zip(&input.c).map(|(l, c)| c*(i as i32 - l)).sum::<i32>()
                };
                last[t] = pre_last;
                if best_satification < tmp_satification {
                    best_contest = t;
                    best_satification = tmp_satification;
                }
            }
            score = best_satification;
            last[best_contest] = i;
            schedule[i as usize] = best_contest;
            ds[best_contest].push(i as usize + 1);
            // println!("{}", best_contest+1);
        };
        State{score, schedule, ds}
    }
    fn changed_score(&self, input: &Input, day: usize, contest: usize) -> Change {
        let old_contest = self.schedule[day];
        let old_p = self.ds[old_contest].iter().position(|x| *x == day+1).unwrap();
        let old_prev = self.ds[old_contest].get(old_p-1).cloned().unwrap_or(0);
        let old_next = self.ds[old_contest].get(old_p+1).cloned().unwrap_or(input.d as usize+1);
        let new_p = self.ds[contest].iter().position(|x| *x > day+1).unwrap_or(self.ds[contest].len());
        let new_prev = self.ds[contest].get(new_p-1).cloned().unwrap_or(0);
        let new_next = self.ds[contest].get(new_p).cloned().unwrap_or(input.d as usize+1);
        let diff = (cost(old_prev, day+1) + cost(day+1, old_next) - cost(old_prev, old_next)) * input.c[old_contest]
                   - (cost(new_prev, day+1) + cost(day+1, new_next) - cost(new_prev, new_next)) * input.c[contest]
                   + input.s[day][contest] - input.s[day][old_contest];
        Change {
            old: Contest {contest: old_contest, p: old_p},
            new: Contest {contest: contest, p: new_p},
            day,
            diff
        }
    }
    fn change(&mut self, change: Change) {
        self.ds[change.old.contest].remove(change.old.p);
        self.ds[change.new.contest].insert(change.new.p, change.day+1);
        self.score += change.diff;
        self.schedule[change.day] = change.new.contest;
    }
    fn one_point_change(&mut self, input: &Input, rng: &mut SmallRng) {
        let day = rng.gen_range(0, input.d) as usize;
        let new_contest = rng.gen_range(0, 26) as usize;
        let change = self.changed_score(input, day, new_contest);
        if change.diff > 0 {
            self.change(change);
        }
    }
    fn swap(&mut self, input: &Input, rng: &mut SmallRng) {
        let d1 = rng.gen_range(0, input.d-1) as usize;
        let d2 = rng.gen_range(d1+1, (d1 + 16).min(input.d as usize) as usize);
        let change1 = self.changed_score(input, d1, self.schedule[d2]);
        let change2 = self.changed_score(input, d2, self.schedule[d1]);
        if change1.diff + change2.diff > 0 {
            self.change(change1);
            self.change(change2);
        }

    }
    fn local_search(&mut self, start: Instant, input: &Input) {
       let mut rng = SmallRng::from_entropy();
       let limit = Duration::from_millis(1975);
       while Instant::now() - start < limit {
           if rng.gen_bool(0.5) {
               self.one_point_change(input, &mut rng);
           } else {
               self.swap(input, &mut rng);
           }
       } 
    }
}

fn cost(a: usize, b: usize) -> i32 {
    let d = b - a;
    (d*(d - 1) / 2) as i32
}
struct Contest {
    contest: usize,
    p: usize
}
struct Change {
    old: Contest,
    new: Contest,
    day: usize,
    diff: i32
}

Measured memory_bytes: 2284