Comment detail

情報オリンピック2007年度世界大会「洪水」 (Nested Flatten)

This comment is reply for 5754 yukoba: 中高生向けの情報オリンピックの世界大会2...(情報オリンピック2007年度世界大会「洪水」). Go to thread root.

プログラムの制限時間もクリアしているバージョン。
以下が擬似コードです。

-------------------------------------

{まだたどっていない壁}がある
  [全ての壁|座標でソート済み]in{まだたどっていない壁}から最初の壁をとる。次はこの配列番号から。
  左手に水:int=頂点から右方向に壁 なら 1 下方向なら -1
  最初の壁に戻ってくるまで、壁をたどる (do-while)
    {たどった壁}に入っている => {たどった壁}から{両方水の壁}へ移動
      else {まだたどっていない壁}から{たどった壁}へ移動
    次の壁の始点=前の壁の終点
    {次の壁の始点 -> つながっている壁|not in 両方水の壁 && 崩壊壁}から「角度」の小さい順にたどる
      角度=外積(前の壁、次の壁)×左手に水(正負)-> 90~360
  {たどった壁} => {崩壊壁}
{両方水の壁}を表示

-------------------------------------
  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
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package jp.yukoba.ioi2007;

import scala.collection.mutable._

object Flood4 extends Application {
  class Base
  case class Point(x:int, y:int) extends Base {
    def < (that:Point) = (y < that.y || (y == that.y && x < that.x))
  }
  case class Wall(no:int, from:Point, to:Point) extends Base {
    def < (that:Wall) = (from < that.from)
  }
  
  // 点番号 -> Point
  val N = readInt
  val dotMap = Map(List.tabulate(N, i => {
    val line = readLine.split(" ").map(Integer.parseInt(_))
    (i+1, Point(line(0), line(1)))
  }):_*)
  // 壁番号 -> Wall
  val W = readInt
  val wallMap = Map(List.tabulate(W, i => {
    val ary = readLine.split(" ").map(Integer.parseInt(_))
    // 左上に近いのを from に設定
    val p1 = dotMap(ary(0))
    val p2 = dotMap(ary(1))
    if(p1 < p2)
      (i+1, Wall(i+1, p1, p2))
    else
      (i+1, Wall(i+1, p2, p1))
  }):_*)
  // 訪れていない壁番号のセット
  val unvisited:Set[int] = Set(wallMap.keys.toList:_*)
  // ソート済みの壁の壁番号の配列
  val sortAry = wallMap.keySet.toList.sort((n1, n2) => 
      wallMap(n1) < wallMap(n2)).toArray
  // 上のたどっていくポインタ
  var sortAryPos = 0
  // {たどった壁} {両方水の壁}
  val visited = Set[int]()
  val bothWater = Set[int]()
  val brokenWall = Set[int]()
  // Point -> Wallの配列
  val point2wallAry = Map[Point, Array[(Wall,int)]]()
  for(pointNo <- dotMap.keys) {
    // 頂点から出る壁は1~4つ
    val point = dotMap(pointNo)
    point2wallAry(point) = new Array[(Wall,int)](4)
  }
  for(wallNo <- unvisited) {
    def put(p:Point, wallDir:(Wall,int)) {
      val ary = point2wallAry(p)
      for(i <- 0 until (ary.length)) {
        if(ary(i) == null) {
          ary(i) = wallDir
          return
        }
      }
      error("ここにはこない")
    }
    val wall = wallMap(wallNo)
    put(wall.from, (wall, 1))
    put(wall.to, (wall, -1))
  }
  
  while(unvisited.size > 0) {
    //println(unvisited)
    // [全ての壁|座標でソート済み]in{まだたどっていない壁}から最初の壁をとる。次はこの配列番号から。
    def getFromSortAry():int = {
        while(true) {
          val wallNo = sortAry(sortAryPos)
        if(unvisited.contains(wallNo)) return wallNo
        sortAryPos += 1
        }
      error("ここにはこない")
    }
    var wallNo = getFromSortAry
    var wall = wallMap(wallNo) 
    // 左手に水:int=頂点から右方向に壁 なら 1 下方向なら -1
    val isLeftWater = if(wall.from.y == wall.to.y) 1 else -1
    //println("isLeftWater = " + isLeftWater)
    // 壁のたどる方向、1 = from -> to、-1 = to -> from
    var wallDir = 1
    // 最初の壁に戻ってくるまで、壁をたどる (do-while)
    val firstWallNo = wallNo
    val firstWallDir = wallDir
    def loop() {
      while(true) {
        //println("wall = " + wall + ", wallDir = " + wallDir)
        //{たどった壁}に入っている => {たどった壁}から{両方水の壁}へ移動
        //  else {まだたどっていない壁}から{たどった壁}へ移動
        if(visited.contains(wallNo)) {
          visited -= wallNo
          bothWater += wallNo
        } else {
          unvisited -= wallNo
          visited += wallNo
        }
        // 次の壁の始点=前の壁の終点
        val nextStart = (if(wallDir == 1) wall.to else wall.from)
        // {次の壁の始点 -> つながっている壁|not in 両方水の壁 && 崩壊壁}から「角度」の小さい順にたどる
        //println(nextStart)
        val wallAry = point2wallAry(nextStart)
        var minArg = Integer.MAX_VALUE
        var minWallDir:(Wall,int) = null
        for(i <- 0 until (wallAry.length)) {
          val nextWallDir = wallAry(i)
          //println("nextWallDir = " + nextWallDir)
          if(nextWallDir != null && !bothWater.contains(nextWallDir._1.no) && 
            !brokenWall.contains(nextWallDir._1.no)) {
            // 角度=外積(前の壁、次の壁)×左手に水(正負)-> 90~360
            def getArg(wall1:Wall, wallDir1:int, wall2:Wall, wallDir2:int, isLeftWater:int):int = {
              def toSign(p:Point):Point = Point(toSignI(p.x), toSignI(p.y))
              def toSignI(i:int) = if(i<0) -1 else if(i==0) 0 else 1
              def toVec(wall:Wall, wallDir:int) =
                if(wallDir == 1) Point(wall.to.x - wall.from.x, wall.to.y - wall.from.y)
                else Point(wall.from.x - wall.to.x, wall.from.y - wall.to.y)
              val vec1 = toSign(toVec(wall1, wallDir1))
              val vec2 = toSign(toVec(wall2, wallDir2))
              if(vec1 == vec2) return 180
              else if(vec1.x == -vec2.x && vec1.y == -vec2.y) return 360
              val gaiseki = (vec1.x * vec2.y - vec1.y * vec2.x) * isLeftWater
              if(gaiseki < 0) return 90
              else return 270
            }
            val arg = getArg(wall, wallDir, nextWallDir._1, nextWallDir._2, isLeftWater)
            //println("arg = " + arg)
            if(arg < minArg) {
              minArg = arg
              minWallDir = nextWallDir
            }
          }
        }
        //assert(minWallDir != null)
        if(minWallDir == null) return
        // 最初の壁に戻って来たら終了
        val nextWall = minWallDir._1
        val nextWallDir = minWallDir._2
        if(nextWall.no == firstWallNo && nextWallDir == firstWallDir) return
        wall = nextWall
        wallDir = nextWallDir
        wallNo = wall.no
      }
      
    }
    loop()
    brokenWall ++= visited
    visited.clear
  }  
  println(bothWater.size)
  for(wallNo <- bothWater)
    println(wallMap(wallNo).no)
  //println(brokenWall.size)
}

Index

Feed

Other

Link

Pathtraq

loading...