競プロ弱者の解答

競プロ弱者の成長記録

キーエンス2020:水色競プロerのムーブ備忘録(Dの考察の流れが良かった)

〇コンテスト名:キーエンスコンテスト2020

〇配点          A:100 B:200 C:400 D:700 E:900 F:1100

〇制限時間  120分

 いわゆるARC級というやつですね。

〇コンテスト開始時のsyunsukeのレート 1266

〇今回の方針 
 配点的に恐らく緑、水、青によるC早解き競争。
 1WA覚悟で速さ優先。チェックは甘めでサブミット!

コンテスト開始!

A:縦(H)横(W)のどちらかを足す。N以上になるまで繰り返す。最短で何回必要か?

 合計をSUM=0 としておいて、HとWの大きい方を足し、足されたなかった方は1を引き・・
 引く必要はなさそうですね。
 最初にHとWのどちらが大きいかを判定し、大きい方を足し続けて何回でN以上になるか?
 なので、NをHとWの大きい方で割り、答えを切り上げれば良いですね。
 pythonで切り上げを行うなら、mathの中にあるceilを使うと良いです。

import math

H=int(input())
W=int(input())
N=int(input())

print(math.ceil(N/(max(H,W))))

 全部のチェックは行わず、速さ優先で提出
 2:40 AC

B:N個のロボットが直線状に並んでいる。ロボットはXi-LiからXi+Liの範囲で動く。ぶつからないように並べられるのは最大何個か?
 [Xi-Li,Xi+Li]をリストで持っておいて、ソートして・・
 いやいや200点問題でそれはない。
 もう一度問題文をよく読んで・・
 でもやっぱり[Xi-Li,Xi+Li]をリストで持っておいて、ソートして・・
 いやいや200点問題でそれはヤバい。

 だんだん焦ってきた。想定解じゃないかもしれないけどこの方針で一旦提出。

 位置をDとして、D=-10**9で初期化
 [[Xi+Li,Xi-Li]をリストに入れて、ソート
 Xi-LiがD以上なら使用できる。DをXi+Liに更新。
 そうでなければぶつかるから使用しない。
 

N=int(input())
L=[]
for i in range(N):
    a,b=map(int,input().split())
    L.append([a+b,a-b])
L.sort()

ans=0
X=-10**9
for i in range(N):
    if L[i][1]>=X:
        ans+=1
        X=L[i][0]
print(ans)

 一応テストケースでチェックして提出
 11: 33AC ヤバイかなり時間をロスした。

C:N個の整数Aiを持つリストで連続するAiの和がSになるものがK個になるものを作れ。
 あれ?KがN以下なら、SをK個並べて、あとは1にでもしとけば良くない?
 いや、Sが1の時に死ぬ。Sが1の時は残りを2にしておけばOK?
 テストケースで確認して提出
 WA!
 ・・そんな単純なわけないですよね。
 でも、なにが悪いか分からない。
 今回のコンテストはこの問題の早解き勝負なのに・・ 焦るばかりで頭が回りません。
 !!!
 1がたくさんならんでいたら、どこかで合計Sになるケースが出てくる!
 S!=10**9なら、SをK個並べて残りは10**9で埋める。
 S==10**9なら、10**9をK個並べて、残りは10**9-1で埋める。

N,K,S=map(int,input().split())

if S!=10**9:
    ANS=[10**9 for i in range(N)]
    for i in range(K):
        ANS[i]=S
else:
    ANS=[10**9-1 for i in range(N)]
    for i in range(K):
        ANS[i]=S
print(*ANS)

上のコードは、初めに10**9もしくは10**9-1をN個並べておいて、あとでK個だけSに書き換える方針です。
 22:54AC 1WA
 恐る恐る順位表を見ると・・ 480番台! 1WAのペナルティを考えても700番台で終われそう?
 って油断してやらかしたのが前回。
 今回は時間いっぱいD問題にかじりつきましょう。

D:N枚のカードがあり、表と裏にそれぞれAiとBiが書かれている。隣のカードと入れ替えて良いので右のカードは左のカードと同じかそれ以上になるように並べる(広義単調増加)ことができるか?できるならその最小入れ替え回数を示せ。
 考察開始。
 まず、偶数番目のカードは、偶数番目にもってくるとAiが上、奇数番目にもってくればBiが上になる。奇数番目のカードはその逆。
 偶数ででてくる数字と最初の番号をいれたヒープEVE、奇数で・・ヒープODD、すでに出た番号を管理するリストCを用意すれば、並べられるかどうかは判定できそう?
 でもその時の入れ替え回数はどうする?
 制約をもう一度確認
 N<=18
 簡単に入れ替え回数を把握する方法は無いから、何らかの全探索をしなさいってことですね。
 だったら・・・
 ・0インデックスで考えて、0は偶数なので偶数位置のカードの数>=奇数位置のカードの数になる。
 ・奇数位置のカードの内、x枚を偶数位置で使用する。偶数位置のカードもx枚奇数位置で使用する。
 ・x枚ずつの変更を終えた後、奇数位置は[数字,最初のカード位置]でリストを作りソート。偶数も同様に
 ・奇数位置のカードからx枚を選ぶ組み合わせを全数探索。偶数位置も同様に。
 ・そして、偶数リスト、奇数リストの順で小さい方から並べる。
 ・全探索!広義単調増加が可能か?可能なら最初の位置と実際の位置の差を取る(実際の位置が最初の位置より前なら、その差を足す!)
 これでいけるのでは?
 祈りながらテストケースを試すと?
 通る!!
 勝利を確信して提出!!!
 ジャッジ開始とほぼ同時にWA!
 方針はどこも間違ってないはずだが?
 凶悪なコーナーケース?
 問題文を読み直してもそんなものは無い。
 ・・・
 ところで、最初の位置と実際の位置の差で本当に移動回数になる?そういえば、なんか転倒数って言葉なかった?念のため調べてみたらそれっぽい!コードを探してペタリ。
 でも、転倒数を調べて、現在の最小値と転倒数の大きさを判定し、転倒数のほうが小さければ最小値を更新。の方針にすると、毎回最小値を0に更新してくれる。何で?
 転倒数を変数tcntに入れて出力すれば・・ちゃんとでてくる。
 だったら、tcntと現在の最小値を比較して・・
 テストケースは通る。
 もうよくわからないし時間もないので、提出。
 あれ?ジャッジが進んでる??
 AC!
 今思えば、転倒数が毎回0になるのは、転倒数を数えた際に、リストを並べ替えていたんでしょうね。なので、転倒数と現在の最小値を比べた際は正しい転倒数。そして、最小値を転倒数に更新する際は、転倒数は0になっていたんでしょう。
 110:09AC 1WA+Cの1WA

残り10分弱ではもうE,Fは解けません。E,Fは開くことなくコンテスト終了
結局110:09 4完2ペナ

パフォ1846,レート+73で1339になりました。

パフォもレートもHighest更新

自己ベストが出たことはもちろん良かったのですが、今回は考察の流れが良かったです。
・Dで奇数と偶数に分けて考えられたこと。
・制約を見て、何らかの全探索であることに気づけたこと。
・転倒数は偶然ですが、コンテスト中は蟻本の目次をすぐに開けるようにしておけば、何かアルゴリズムが必要そう?ってなった時に役立つかもしれません。
今日のコンテストからはそうしましょう。

 提出したDのコードはこれです。随分無駄の多いコードですが。

N=int(input())
A=list(map(int,input().split()))
B=list(map(int,input().split()))

o=N//2
e=N-o

import itertools
E=[]
O=[]
for i in range(N):
    if i%2==0:
        E.append(i)
    else:
        O.append(i)
ANS=10000

def mergecount(Z):
    cnt1 = 0
    z = len(Z)
    if z>1:
        A1 = Z[:z>>1]
        A2 = Z[z>>1:]
        cnt1 += mergecount(A1)
        cnt1 += mergecount(A2)
        i1=0
        i2=0
        for i in range(z):
            if i2 == len(A2):
                Z[i] = A1[i1]
                i1 += 1
            elif i1 == len(A1):
                Z[i] = A2[i2]
                i2 += 1
            elif A1[i1] <= A2[i2]:
                Z[i] = A1[i1]
                i1 += 1
            else:
                Z[i] = A2[i2]
                i2 += 1
                cnt1 += z//2 - i1
    return cnt1

for i in range(o+1):
    X=list(itertools.combinations(E,i))
    Y=list(itertools.combinations(O,i))
    for j in range(len(X)):
        for k in range(len(Y)):
            ev=[]
            od=[]
            for n in range(N):
                if n%2==0:
                    if n in X[j]:
                        od.append([B[n],n])
                    else:
                        ev.append([A[n],n])
                else:
                    if n in Y[k]:
                        ev.append([B[n],n])
                    else:
                        od.append([A[n],n])
            ev.sort()
            od.sort()
            #print(ev,od)
            Z=[]
            Num=0
            F=0
            for x in range(N):
                if x%2==0:
                    if Num>ev[x//2][0]:
                        F=1
                        break
                    else:
                        Num=ev[x//2][0]
                        Z.append(ev[x//2][1])
                else:
                    if Num>od[x//2][0]:
                        F=1
                        break
                    else:
                        Num=od[x//2][0]
                        Z.append(od[x//2][1])
            if F==0:
                #print(Z)
                tcnt=mergecount(Z)
            if F==0 and tcnt<ANS:
                ANS=tcnt
                
if ANS==10000:
    print(-1)
else:
    print(ANS)