0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

swiftだってできるもん!【競技プログラミング】

Last updated at Posted at 2024-09-15

はじめに

  • ABC371c問題は、c問題のくせに難解でした。後から調べたら、コンテスト時間中にACしたswift使いは皆無。元々、参加してるswift使いは10人未満だけどね。
  • で、c問題がswift使いに解けなかった理由の一つに、2つのワナが仕掛けられていたから、というのもある気がしたので、それを説明したい!
  • ...、いや、ゴメンなさい。単なる言い訳です。でも、一応、言わせて欲しい。

問題の紹介

  • 詳細は問題文を見て貰うとして、概要はこんな感じ
    • 頂点の数が同じグラフが2つある(G,H)
    • それぞれのグラフは無指向辺をそれぞれ、Mg,Mh本だけ持つ
    • グラフHの辺を削除したり追加したりして、グラフGと同型としたい
    • 辺(i,j)《頂点iと頂点jを結ぶ辺》の追加・削除に際して、Aijのコストがかかる。
    • 最小のコストはいくらとなるか?
    • 制約は、$「N≦8」「0≦Aij≦10^6」$

罠(その1) -- swiftにはnext_permutationがない!

  • 今回の問題のキモは、N≦8による、計算量を気にしない全探査だったんだよね。
  • で、全探査をするに当たっては、グラフGとグラフHを同型とするための頂点の変換処理をする必要があるけど、頂点の数が8だから、グラフGとグラフHの頂点を全てマッチさせる場合でも、8!(=40320)の組み合わせで済むんだよね。
  • コンテスト中は問題文見ただけで諦めたから、Nが最大8と言うことまで気がつかなかったよ。でも、仮に気がついたとしても、8!の組み合わせを作り出せるのか?c++なら関数next_permutationでサクッと作れるけど、swiftにはそんな便利な関数がないんよね。
  • まぁ、僕は以前作ったけどね(ドヤァ
  • 実は、どや顔出来るほどの大したものではないし、出来るのも下記程度。
let P = [0,1,2]
let Ps = pmt_gen(a)

print(b) // [[0, 1, 2], [1, 0, 2], [2, 0, 1], [0, 2, 1], [1, 2, 0], [2, 1, 0]]
print(b.count) // 6
  • こんな単純な関数なんだから、標準で用意しておいてよ、swiftさん。
  • でもまぁ、とりあえず、罠は回避した!

罠(その2) -- swiftの配列のcontainsメソッドはタプルを認識しないんだよ!

  • ΩΩΩ < な....なんだってェ−−!!
  • あ…ありのまま 今 起こった事を話すぜ!

    • で、containsがタプルを認識しないと何が問題かというと、今回の問題は、グラフ理論のように見えてグラフ理論は関係ないから、「辺」も単純なタプルで格納して扱うからです。
    • で、「グラフGが持っている辺(タプル)の配列」と「グラフHが持っている辺(タプル)の配列」の有無を比較して、不一致した時だけコストを発生させる簡単なお仕事です。
    • で、有無の一致を確認するときに、H、Gの配列にそれぞれ、(i,j)、(p(i)、p(j)){p in Ps(罠(その1)のPs)}の辺が含まれているかを確認し、「共に持っている、共に持ってない」ときは、コストゼロ。「片方だけ持っている」ときは、コストAijが発生と言うこと。
    • で、配列中に指定の要素が含まれているか調べるのに、containsを使用する...のに、containsメソッドはタプルを認識しないんだよ!
  • まぁ、タプルをcontains対応させる一工夫で、次の呪文をマスターしたから、問題ないけどね。
extension Sequence {
    func contains<T:Equatable,U:Equatable>(_ element: (T, U)) -> Bool {
        return (self as! [(T, U)]).contains { $0 == element }
    }
}

let tpls = [(1,2),(3,4)]
print(tpls.contains((1,2))) // true
print(tpls.contains((2,3))) // false

swiftだってできるもん!

  • c++陣営から仕掛けられた罠2つを解除したので、以下のとおり解くことが出来た。
func pmt_gen<T>(_ ary: [T]) -> Set<[T]> {・・・}
extension Sequence {・・・}

let N = Int(readLine()!)!

let Mg = Int(readLine()!)!
var Gs : [(Int,Int)] = []
for _ in 0..<Mg {
    let (a,b) = [readLine()!.split(separator:" ").map{Int($0)!}].map{($0[0]-1,$0[1]-1)}[0]
    Gs += [(a,b),(b,a)] // 変換配列pにより変換されたの座標p(i),p(j)について、p(i)<p(j)と限らないので、(b,a)も用意しておく。
}

let Mh = Int(readLine()!)!
var Hs : [(Int,Int)] = []
for _ in 0..<Mh {
    let (a,b) = [readLine()!.split(separator:" ").map{Int($0)!}].map{($0[0]-1,$0[1]-1)}[0]
    Hs += [(a,b)] // Hsについては、a<bでしか走査しないので、(b,a)不要。
}

var A = [[Int]](repeating:[Int](repeating:0,count:N),count:N)
for i in 0..<(N-1) {
    let vs = readLine()!.split(separator:" ").map{Int($0)!}
    for (j,v) in vs.enumerated() {
        A[i][i+1+j] = v // Aijに逆三角形の形で格納
    }
}

var P = (0..<N).map{$0} // [0,1,2,3,...]
var Ps = pmt_gen(P) // [[0,1,2,3,...], [1,0,2,3,...], [2,0,1,3,...], [...

var ans = Int.max

for p in Ps { // N!パターンの総当たり。でも、最大で8!だから大したことない。
    var sum = 0
    for i in 0..<N {
        for j in i..<N {
        
        if Hs.contains((i,j)) != Gs.contains((p[i],p[j])) { sum += A[i][j] } 
            
        }
    }
    ans = min(ans,sum)
}

print(ans)
  • 上記でも計算量的には大したことない。
  • 一番外側のforループで$N!$、内側のforループ2つで$N^2$、配列のcontainsがO(N)だから、全体の計算量は$O(N!・N^3)$。いつもの問題なら恐ろしい計算量に見えるけど、N=8だから、単純計算で$2×10^7$。あれ?結構ギリじゃん!でも、100ms未満でACできたよ。
  • Hs,Gsを配列でなくて、集合で作ればcontainsはO(logN)になるのかな?
  • よーしパパ集合で作っちゃうぞ〜...
var Gs = Set<(Int,Int)>()
- error: type '(Int, Int)' does not conform to protocol 'Hashable'
  • 今度は、Hashableかよ!

さいごに

  • 最後の最後にタプルがオチをつけてくれました。本当に、どうにかして欲しいね。
  • あ、ちなみにタプルをhashableにするためには、hash(into:)メソッドに対応させる必要があるけど、タプルはextensionが使えないので、個別に構造体を作るしかないです。
  • 以前、タプルをComparableにしたときの構造体にhashメソッドを追加すればいい。今回の問題がTLEだったら対応してたけど、結果はACだったし、今日は疲れたからもういいや。
0
1
2

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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?