ABC 080
Atcoder Beginner Contest 080
C - Shopping Street
問題
- 自店の営業時間を決める
- 月~金、AM・PM の10区分の時間帯がある
- 同じ商店街の他の 個の店の営業時間は既に決まっている
- 「自店と店 が共通して営業している時間帯の数 」によって、自店が得られる利益 が決まっている
- 自店の総利益は、 の全他店 の和
- 総利益を最大化せよ
解法
通りなので、全ての営業時間の組み合わせを全探索。店の数も100以下なので、間に合う。
営業時間を10桁の2進数で表すと、「自店と他店の共通して営業している時間帯の数」は、「自店の営業時間を表す数と、他店の営業時間を表す数の、AND(論理積)を取った後に立っているビット数」と表せる。
| 月AM | 月PM | 火AM | 火PM | 水AM | 水PM | 木AM | 木PM | 金AM | 金PM | 2進数表現 | 共通 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 自店 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1010110011 | |
| 他店 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0111011011 | |
| AND | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0010010011 | 4 |
こうすると、自店の営業時間の全探索も「1~1023の数値の全探索」で表現でき、1回の計算も配列で持つよりは速い。
pythonでは、立っているビット数を直接得られる関数は無いが、2進数テキストに変換して'1'の数を数えるのが速いらしい。
bin(x).count('1')
1 2 3 4 5 6 7 8 9 10 |
from operator import muln = int(input())cof = [1 << x for x in range(10)]fs = [sum(map(mul, map(int, input().split()), cof)) for _ in range(n)]ps = [list(map(int, input().split())) for _ in range(n)]ans = float('-inf')for g in range(1, 1024): ans = max(ans, sum(p[bin(f & g).count('1')] for f, p in zip(fs, ps)))print(ans) |
D - Recording
問題
- 個のテレビ番組を録画機で全て録画
- 録画機は、あるチャンネルの時刻 から時刻 までを録画するとき、時刻 から、他のチャンネルの録画には使えない
- つまり、同じチャンネルなら番組の時刻が連続していても録画できるが、違うチャンネルなら時刻0.5以上間隔が無いと録画できない
- 録画機はいくつ必要?
解法
はじめに思いついた解法(WA)
- 番組を開始時刻順にソート
- 優先度キューを使って、早く終わる録画機から次の番組を録画
- 一番早く終わる録画機でも次の番組を録画できない場合、優先度キューに追加(キューの要素数が1増える)
- 最終的な優先度キューの要素数が、必要な録画機数
だが、これだと次のような場合を捉えきれない。
- 時刻で録画が終わる録画機が複数個ある
- チャンネルを録画中とする
- 次にちょうど時刻からチャンネルで始まる番組があった場合
- 本来は、を録画中の録画機で続けて録画できるため、増やさなくてよい
- 優先度キューから先に出されるのはを録画中の録画機
- 録画不可と判断して、録画機を1つ増やしてしまう
なので、以下の応急的な事前処理を施して、AC
- 同じチャンネルで連続している番組は、繋げて1番組とする
ソートを2回もしているため、効率悪い。もっと良い解法がある気がする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
from heapq import heapreplace, heappushn, v = map(int, input().split())programs = [tuple(map(int, input().split())) for _ in range(n)]programs.sort()tmp = [None] * vcombined = []for s, t, c in programs: c -= 1 prev = tmp[c] if prev is not None: ps, pt, pc = prev if pt == s: tmp[c] = (ps, t, c) else: combined.append((ps, pt, pc)) tmp[c] = (s, t, c) else: tmp[c] = (s, t, c)for prev in tmp: if prev is not None: combined.append(prev)combined.sort()recorders = [(-1, None)]for s, t, c in combined: end, channel = recorders[0] if end < s or c == channel: heapreplace(recorders, (t, c)) else: heappush(recorders, (t, c))print(len(recorders)) |
解法2
時間の範囲がたかだかなので、imos法が使えるようだ。
- のサイズの配列 を作る
- 同じチャンネルの連続した番組はまとめる
- 番組の開始・終了時刻を とすると、
- に1加算
- から1減算
- 全番組を処理後、 の累積和をとり、その中の最大値が答え
加算と減算するのインデックスにちょっと迷ったが、時刻 が含まれない時は でよい。
こちらの方が少し速い。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
from collections import defaultdictfrom itertools import accumulaten, v = map(int, input().split())programs = defaultdict(list)for _ in range(n): s, t, c = map(int, input().split()) programs[c].append((s, t))imos = [0] * (10 ** 5 + 1)for c, p in programs.items(): p.sort() ps, pt = p[0] for s, t in p[1:]: if pt == s: pt = t else: imos[ps - 1] += 1 imos[pt] -= 1 ps, pt = s, t imos[ps - 1] += 1 imos[pt] -= 1print(max(accumulate(imos))) |

