CODE FESTIVAL 2014 Easy D - 枕決め
知らないと解けないなと思ったのでメモ。参考
CODE FESTIVAL 2014 あさプロMiddle : B - 枕決め - kmjp's blog
N人の人とM個の枕がある。
N人はそれぞれ高さがX[i]以上Y[i]以下の枕が好みであり、そのような枕を1個使いたい。
M個の枕の高さA[0] ~ A[m]が与えられるとき、最大何人が好みの枕を使えるか。
解き方
まず (X[i], Y[i]) を List[x[i]].append(Y[i]) として前処理する。
次に高さの下限1から上限100000まで以下の処理を行う。今見ている高さをhとすると:
最初に作った配列を参照し、X[i]==hであるような人がいたら、(List[h] != [ ] であるなら)
List[h] の要素を heap に入れる。
heap から A[i]==h な枕の数だけ、heap からY[i]の低い順に枕を割り当てる。
heap には「h以上の枕が好みの人が、Y[i]の低い順に入っている」という状態にしておくことで、以後登場する枕をY[i]の低い順に割り当てることができる。
X ≦ Y であり、h(X) の小さい順に調べているので、heappop したときに、Y[i] ≧ h ならば
その i さんは X[i] ≦ h ≦ Y[i] を満たす枕が存在し、割り当てることが最適といえる。
逆に、Y[i] < h ならば好みの枕は割り当てないことが最適といえる。
import sys read = sys.stdin.buffer.read readline = sys.stdin.buffer.readline readlines = sys.stdin.buffer.readlines from heapq import heapify,heappop,heappush n,m = map(int,readline().split()) xy = [list(map(int,readline().split())) for i in range(n)] a = [int(readline()) for i in range(m)] A = [0]*100001; Y = [[] for i in range(100001)] for i in a: A[i] += 1 for x,y in xy: Y[x].append(y) que = []; heapify(que) ans = 0 for h in range(1,100001): for y in Y[h]: heappush(que,y) while A[h]: if que == []: break yy = heappop(que) if yy >= h: ans += 1 A[h] -= 1 print(ans)
abc 187 E - Through Path
"「ある頂点の子孫でない頂点に xを足す」は、
「根の子孫に xを足す」と「ある頂点の子孫に -xを足す」に分けることができるので、
全てのクエリを「ある頂点の子孫に x を足す」の形に帰着することができます。"
かしこい。は?となりながら解説の言うとおりにやってみたら確かにできた。
2つ条件があって、一つが簡単に書けるときは、もう一方をその条件に帰着できないか考えるのが大事なのかなと思った。あと木の上で総和を求めていくのも初だった。
import sys input = sys.stdin.readline from collections import deque n = int(input()) h = [0]*n g = [[] * n for i in range(n)] for i in range(n-1): u, v = map(int, input().split()) u -= 1 v -= 1 g[u].append(v) g[v].append(u) h[i] = (u,v) que = deque() que.append((0,1)) dep = [0]*n chk = [False]*n chk[0] = True cnt = 1 while que: now,p = que.popleft() if cnt == n: break for i in g[now]: if not chk[i]: dep[i] = p chk[i] = True cnt += 1 que.append((i,p+1)) ans = [0]*n q = int(input()) for i in range(q): t,e,x = map(int, input().split()) e -= 1 a,b = h[e] if t == 2: a,b = b,a if dep[a] < dep[b]: ans[b] -= x ans[0] += x else: ans[a] += x que = deque() que.append(0) chk = [False]*n chk[0] = True cnt = 1 while que: now = que.popleft() if cnt == n: break for i in g[now]: if not chk[i]: ans[i] += ans[now] chk[i] = True cnt += 1 que.append(i) print(*ans,sep='\n')
abc 100 D - Patisserie ABC (python)
解けなかった。
発想1.(Xi + Xj + Xk) + (Yi + Yj + Yk) + (Zi + Zj + Zk) を
(Xi + Yi + Zi) + (Xj + Yj + Zj) + (Xk + Yk + Zk) とみて考える。
これは添え字が同じになるように移項するのと同じ発想なので思いつくべきだった。
発想2.- (Xi + Xj + Xk) + (Yi + Yj + Yk) - (Zi + Zj + Zk)
= (- Xi + Yi - Zi) + (- Xj + Yj - Zj) + (- Xk + Yk - Zk)
確かにとはなったけど、自力では厳しい考えだった。
なので、X, Y, Z それぞれが + か - かの8通りで ± X ± Y ± Z を求めておいて、降順ソートして m 個の和を求めたうちの最大値を出せば終わり。
numpy 使うと楽に書ける。
import sys read = sys.stdin.buffer.read readline = sys.stdin.buffer.readline readlines = sys.stdin.buffer.readlines import numpy as np n,m = map(int,readline().split()) chk = np.empty((n,8),dtype=np.int64) # n行8列の初期リスト for i in range(n): a,b,c = map(int,readline().split()) chk[i][0] = a+b+c chk[i][1] = a+b-c chk[i][2] = a-b+c chk[i][3] = a-b-c chk[i][4] = -a+b+c chk[i][5] = -a-b+c chk[i][6] = -a+b-c chk[i][7] = -a-b-c chk = np.sort(chk,axis=0)[::-1] # 8列分一括で列ごとに降順ソート chk = np.resize(chk,(m,8)) # 上からm個まであればいい print(max(np.sum(chk,axis=0))) # 列ごとに合計値求めた中の最大値が答え
abc 104 c All Green (python)
ずっとほったらかしていた問題。
dfs で全探索するのだけど、愚直に計算すると間に合わない。
• 中途半端に解く配点は 1 種類以下であり、それ以外の配点は完全に解くかまったく解かない。
• 中途半端に解く配点があるなら、それは完全に解く配点以外の配点の中で最も高い配点である。
の2点に気づいて探索する候補を絞る必要がある。
実装は dfs(a,b,c,d) として、
a = 今解こうとしている配点
b = 今まで解いた問題の配点の総和
c = 今まで解いた問題数
d = 中途半端に解いたことがあるか
で全探索した。2つ目の条件を上手く満たすように配点を高い順に a を調べた。
import sys read = sys.stdin.buffer.read readline = sys.stdin.buffer.readline readlines = sys.stdin.buffer.readlines sys.setrecursionlimit(10**7) n,k = list(map(int,readline().split())) k //= 100 l = [] for i in range(n): array = list(map(int,readline().split())) l.append(array) ans = float('inf') def f(a,b,c,d): global ans if a == 0: if b >= k: ans = min(ans,c) return if c >= ans: return if b >= k: ans = min(ans,c) return else: if d == 0: g = min(l[a-1][0],(k-b)//a+2) return [f(a-1,b+a*i,c+i,1) for i in range(1,g)],f(a-1,b,c,d),f(a-1,b+a*(l[a-1][0])+(l[a-1][1])//100,c+l[a-1][0],d) else: return f(a-1,b,c,d),f(a-1,b+a*(l[a-1][0])+(l[a-1][1])//100,c+l[a-1][0],d) f(n,0,0,0) print(ans)
abc 175 E - Picking Goods (python)
3次元 dp するときに、r,c,k のうち k だけ、計算量が小さいときは、
dp = [[[0]*c for _ in range(r)] for _ in range(k)]
と書いた方が実行時間が短くなるという知見を得た。というかこう書かないと TLE するのだがなぜ?
def main(): import sys r,c,k = map(int, input().split()) p= [[0]*c for _ in range(r)] for s in sys.stdin.readlines(): x,y,z = map(int,s.split()) p[x-1][y-1] = z ans = [[[0]*c for _ in range(r)] for _ in range(4)] if p[0][0] > 0: ans[1][0][0] = p[0][0] for R in range(r): for C in range(c): for d in range(4): a = ans[d][R][C] if R < r-1: ans[0][R+1][C] = max(a,ans[0][R+1][C]) ans[1][R+1][C] = max(a+p[R+1][C],ans[1][R+1][C]) if C < c-1: ans[d][R][C+1] = max(a,ans[d][R][C+1]) if d < 3: ans[d+1][R][C+1] = max(a+p[R][C+1],ans[d+1][R][C+1]) dp = 0 for i in range(4): dp = max(ans[i][-1][-1],dp) print(dp) if __name__ == '__main__': main()
※TLEするやつ↓。p の順番を変えただけ。
Submission #18495208 - AtCoder Beginner Contest 175
abc 176 D - Wizard in Maze (python)
atcoder 再開。水色になった。
01-bfs というやつらしい。+0 を append で、+1 に遷移するものを appendleft でdeque に入れてあげると、O(1) で heap みたいなことができる。heap で回したら TLE した。
よくある bfs や今回初めて書いた 5*5 の移動をメモしたかったのでまとめた。
import sys input = sys.stdin.readline h,w = map(int,input().split()) sh,sw = map(int,input().split()) gh,gw = map(int,input().split()) a = [list(input()) for i in range(h)] d = [[10**6]*w for i in range(h)] dh = [1, 0, -1, 0] dw = [0, 1, 0, -1] dx = [0,0,1,1,1,1,2,2,2,2,2,-1,-1,-1,-1,-2,-2,-2,-2,-2] dy = [2,-2,2,1,-1,-2,-2,-1,0,1,2,2,1,-1,-2,2,1,0,-1,-2] from collections import deque que = deque() que.append((sh-1,sw-1,0)) d[sh-1][sw-1] = 0 while que: y,x,p = que.pop() if y == gh and x == gw: break for i in range(4): nw = x + dw[i] nh = y + dh[i] if 0 <= nw < w and 0 <= nh < h and a[nh][nw] != "#" and d[nh][nw] > p: que.append((nh,nw,p)) d[nh][nw] = p for j in range(20): nx = x + dx[j] ny = y + dy[j] if 0 <= nx < w and 0 <= ny < h and a[ny][nx] != "#" and d[ny][nx] > p+1: que.appendleft((ny,nx,p+1)) d[ny][nx] = p+1 if d[gh-1][gw-1] == 10**6: print(-1) else: print(d[gh-1][gw-1])
abc 061 D - Score Attack (python)
ほぼこれ。abc 137 E - Coins Respawn (python) - aacord’s memo
頂点1からNまでに通りうる経路だけ抜き出して、その中で bellman-ford を使う。更新回数が m 回をこえたら inf を出力する。
from collections import deque def belman(s,n,w,es): d = [-float("inf")] * n #d[i] : s→iの最短距離 d[s] = 0 c = 0 while True: update = False for p,q,r in es: if d[p] != -float("inf") and d[q] < d[p] + r: d[q] = d[p] + r update = True c += 1 if c > w: return 'inf' if not update: break return d[n-1] def use_path(n,start,es): q = deque() chk = [False] * n q.append(start) chk[start] = True used = {start} while len(q) > 0: node = q.popleft() for nex in es[node]: if not chk[nex]: chk[nex] = True q.append(nex) used.add(nex) return used n,w = map(int,input().split()) es = [] l = [[] for i in range(n)] r = [[] for i in range(n)] for i in range(w): a,b,c = map(int, input().split()) a -= 1 b -= 1 l[a].append(b) r[b].append(a) es.append((a,b,c)) use = use_path(n,0,l) & use_path(n,n-1,r) ess = [(a,b,c) for a,b,c in es if a in use and b in use] print(belman(0,n,w*2,ess))