結果
問題 |
No.5021 Addition Pyramid
|
ユーザー |
|
提出日時 | 2025-02-25 21:04:33 |
言語 | Kotlin (2.1.0) |
結果 |
RE
|
実行時間 | - |
コード長 | 18,281 bytes |
コンパイル時間 | 20,274 ms |
コンパイル使用メモリ | 565,156 KB |
実行使用メモリ | 58,024 KB |
スコア | 0 |
最終ジャッジ日時 | 2025-02-25 21:05:20 |
合計ジャッジ時間 | 39,872 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | RE * 50 |
ソースコード
import java.io.* import java.nio.file.Paths import java.util.ArrayDeque import kotlin.math.abs import kotlin.math.max fun next() = readLine()!! fun nextInt() = next().toInt() fun nextLong() = next().toLong() fun nextDouble() = next().toDouble() fun nextList() = next().split(" ") fun nextIntList() = next().split(" ").map { it.toInt() } fun nextLongList() = next().split(" ").map { it.toLong() } fun nextDoubleList() = next().split(" ").map { it.toDouble() } data class Coord(val x: Int, val y: Int) data class CommuteInfo(val from: Coord, val to: Coord) // 方向 val delta = arrayOf(Coord(1, 0), Coord(-1, 0), Coord(0, 1), Coord(0, -1)) enum class Env { Local, Prod } data class CalcResult( // インデックス val index: Int = 0, // 最短経路 val path: List<Coord> = emptyList(), // 建築に必要なターン val buildTurn: Int = 0, // 1ターンあたりの利益 val benefitPerTurn: Int = 0, // この経路の建設を開始するために必要な最小金額 val buildableMinMoney: Int = 0, // この経路の建設を開始するために必要な最小ターン val buildableMinTurn: Int = 0, // 建てられるようになったら建てる、をした場合の最終ターンまでに出る利益、建てられない場合は0 val benefitToLastTurn: Int = 0, // この経路を立てた場合に、新たに駅の範囲に入るインデックス val newConnected: List<BuildableOtherInfo> = emptyList() ) // 既に建築されている駅の範囲に入っている端と、もう片方の端の情報 data class BuildableOtherInfo( val index: Int, val other: Coord, ) // 建築された駅の情報 data class StationInfo( // 座標 val coord: Coord, // 残っている建築可能な方向 val directions: MutableList<Coord>, ) fun solve() { val (N, M, K, T) = nextIntList() val commuteInfoList = mutableListOf<CommuteInfo>() // 端ともう片方の端の情報 val coordInfo = Array(N) { Array(N) { mutableListOf<Pair<Int, Coord>>() } } for (i in 0 until M) { val (si, sj, ti, tj) = nextIntList() commuteInfoList.add(CommuteInfo(Coord(si, sj), Coord(ti, tj))) coordInfo[si][sj].add(Pair(i, Coord(ti, tj))) coordInfo[ti][tj].add(Pair(i, Coord(si, sj))) } // #: 通れない val wall = Array(N) { Array(N) { "" } } // 残りターン数 var remainingTurn = T // 所持金額 var money = K // 1ターンに増減する金額 var moneyDelta = 0 // 片方が既に建築されている駅の範囲にあるもう片方の座標 val buildableOther = mutableListOf<BuildableOtherInfo>() // 既に建築されている駅の情報 val stationInfo = mutableListOf<StationInfo>() // 既につながっているindex val connected = mutableSetOf<Int>() // 初期解生成 val pathAndBenefit = calculatePathAndBenefit(M, N, commuteInfoList, T, money, coordInfo) val p = pathAndBenefit.filter { it.benefitToLastTurn != 0 && it.newConnected.size >= 2 }.maxByOrNull { it.benefitPerTurn } if(p != null) { println("# build start: ${p.index}") val path = p.path val pathString = convertPathToString(path) for (i in 1 until pathString.size - 1) { println(pathString[i]) } println(pathString.first()) println(pathString.last()) for (c in path) { wall[c.x][c.y] = "#" } connected.add(p.index) // 駅情報の更新 val startDirection = Coord(path[1].x - path[0].x, path[1].y - path[0].y) stationInfo.add(StationInfo(path[0], delta.filter { it != startDirection }.toMutableList())) val endDirection = Coord(path[path.size - 2].x - path[path.size - 1].x, path[path.size - 2].y - path[path.size - 1].y) stationInfo.add(StationInfo(path[path.size - 1], delta.filter { it != endDirection }.toMutableList())) // 駅範囲に入っている座標情報の更新 buildableOther.addAll(p.newConnected) money -= p.buildableMinMoney moneyDelta += p.benefitPerTurn remainingTurn -= p.buildTurn println("# build end index: ${p.index}") println("# connected: ${connected.sorted().joinToString(", ")}") println("# remain: ${buildableOther.map { it.index }.sorted().joinToString(" ")}") } var reCalcFlag = false var result = calcB(N, remainingTurn, money, commuteInfoList, moneyDelta, buildableOther, stationInfo, wall, connected) while (remainingTurn > 0) { if(reCalcFlag){ result = calcB(N, remainingTurn, money, commuteInfoList, moneyDelta, buildableOther, stationInfo, wall, connected) reCalcFlag = false } val p2 = result.sortedBy { it.benefitToLastTurn }.lastOrNull { it.benefitToLastTurn > 0 } if(p2 != null){ if(p2.path.size == 1){ connected.add(p2.index) reCalcFlag = true continue } println("# build start: ${p2.index}") if (p2.buildableMinTurn != 0) { repeat(p2.buildableMinTurn) { money += moneyDelta remainingTurn-- println(-1) } } val path = p2.path val pathString = convertPathToString(path) for (i in 1 until pathString.size - 1) { println(pathString[i]) } println(pathString.last()) for (c in path) { wall[c.x][c.y] = "#" } connected.add(p2.index) buildableOther.removeAll{ it.index == p2.index } stationInfo.find { it.coord == path[0] }?.directions?.remove(Coord(path[1].x - path[0].x, path[1].y - path[0].y)) val endDirection = Coord(path[path.size - 2].x - path[path.size - 1].x, path[path.size - 2].y - path[path.size - 1].y) stationInfo.add(StationInfo(path[path.size - 1], delta.filter { it != endDirection }.toMutableList())) val newStation = path[path.size - 1] // 新しい駅範囲に入っている座標情報の更新 for(i in -2 until 3) { for (j in -2 until 3) { if (abs(i) + abs(j) <= 2) { val x = newStation.x + i val y = newStation.y + j if (x in 0 until N && y in 0 until N) { for (c in coordInfo[x][y]) { val (index, other) = c if (buildableOther.map { it.index }.contains(index)) { continue } if (!connected.contains(index)) { buildableOther.add(BuildableOtherInfo(index, other)) } } } } } } money -= p2.buildableMinMoney moneyDelta += p2.benefitPerTurn remainingTurn -= p2.buildTurn reCalcFlag = true println("# build end index: ${p2.index}") println("# connected: ${connected.sorted().joinToString(", ")}") println("# remain: ${buildableOther.map { it.index }.sorted().joinToString(" ")}") } else { remainingTurn-- money += moneyDelta println(-1) } } } // 駅の範囲に入っている座標のペアへの経路を探索し、最短経路や利益などの情報を計算する fun calcB( N: Int, remainingTurn: Int, money: Int, commuteInfoList: List<CommuteInfo>, moneyDelta: Int, buildableOther: List<BuildableOtherInfo>, stationInfo: List<StationInfo>, wall: Array<Array<String>>, connected: MutableSet<Int> ): List<CalcResult>{ val dist = Array(N) { IntArray(N) { -1 } } val before = Array(N) { Array(N) { Coord(-1, -1) } } val queue = ArrayDeque<Coord>() val endPoint = buildableOther.filter { !connected.contains(it.index) }.map { it.other }.toMutableList() for(s in stationInfo){ dist[s.coord.x][s.coord.y] = 0 for(d in s.directions){ val dx = s.coord.x + d.x val dy = s.coord.y + d.y if (dx in 0 until N && dy in 0 until N) { if (dist[dx][dy] == -1) { if (wall[dx][dy] == "") { dist[dx][dy] = dist[s.coord.x][s.coord.y] + 1 before[dx][dy] = s.coord queue.add(Coord(dx, dy)) } } } } } while (queue.isNotEmpty()) { val v = queue.removeFirst() if(endPoint.contains(v)){ endPoint.remove(v) if(endPoint.isEmpty()){ break } continue } for (d in delta) { val dx = v.x + d.x val dy = v.y + d.y if (dx in 0 until N && dy in 0 until N) { if (dist[dx][dy] == -1) { if (wall[dx][dy] == "") { dist[dx][dy] = dist[v.x][v.y] + 1 before[dx][dy] = v queue.add(Coord(dx, dy)) } } } } } val result = mutableListOf<CalcResult>() for(e in buildableOther.filter { !connected.contains(it.index) }){ if (dist[e.other.x][e.other.y] == -1) { continue } // 経路復元 val path = mutableListOf<Coord>() var c = e.other while (dist[c.x][c.y] > 0) { path.add(c) c = before[c.x][c.y] } path.add(c) path.reverse() // 1ターンあたりの利益 val benefitPerTurn = abs(commuteInfoList[e.index].from.x - commuteInfoList[e.index].to.x) + abs(commuteInfoList[e.index].from.y - commuteInfoList[e.index].to.y) // 建築に必要なターン val buildTurn = path.size - 1 // この経路の建設を開始するために必要な最小金額 val buildableMinMoney = (100 - moneyDelta) * (buildTurn - 1) + (5000 - moneyDelta) * 1 + moneyDelta // この経路の建設を開始するために必要な最小ターン val buildableMinTurn = if (buildableMinMoney <= money) { 0 } else if (moneyDelta == 0) { -1 } else { (buildableMinMoney - money + moneyDelta - 1) / moneyDelta } // 建てられるようになったら建てる、をした場合の最終ターンまでに出る利益、建てられない場合は0 val benefitToLastTurn = if (remainingTurn <= (buildTurn + buildableMinTurn)) { 0 } else if (buildableMinTurn == -1) { 0 } else { max(0, (remainingTurn - buildTurn - buildableMinTurn) * benefitPerTurn - (100 * (buildTurn - 2) + 5000)) } result.add(CalcResult(e.index, path, buildTurn, benefitPerTurn, buildableMinMoney, buildableMinTurn, benefitToLastTurn)) } return result } // 初期解を生成する fun calculatePathAndBenefit( M: Int, N: Int, commuteInfoList: List<CommuteInfo>, T: Int, money: Int, coordInfo: Array<Array<MutableList<Pair<Int, Coord>>>>, ): Array<CalcResult> { val result = Array(M) { CalcResult() } for (i in 0 until M) { // 幅優先探索で最短経路を計算 val dist = Array(N) { IntArray(N) { -1 } } val before = Array(N) { Array(N) { Coord(-1, -1) } } val queue = ArrayDeque<Coord>() val start = commuteInfoList[i].from val end = commuteInfoList[i].to queue.add(start) dist[start.x][start.y] = 0 while (queue.isNotEmpty()) { val v = queue.removeFirst() if (v.equals(end)) { break } for (d in delta) { val dx = v.x + d.x val dy = v.y + d.y if (dx in 0 until N && dy in 0 until N) { if (dist[dx][dy] == -1) { dist[dx][dy] = dist[v.x][v.y] + 1 before[dx][dy] = v queue.add(Coord(dx, dy)) } } } } // 経路復元 val path = mutableListOf<Coord>() var c = end while (c != start) { path.add(c) c = before[c.x][c.y] } path.add(start) path.reverse() // 1ターンあたりの利益 val benefitPerTurn = abs(start.x - end.x) + abs(start.y - end.y) // 建築に必要なターン val buildTurn = path.size // この経路の建設コスト val buildableMinMoney = 100 * (buildTurn - 2) + 10000 // 最終ターンまでに出る利益、建てられない場合は0 val benefitToLastTurn = if(money < buildableMinMoney){ 0 }else{ (T - buildTurn) * benefitPerTurn - buildableMinMoney } // 新たに範囲に入るindex val buildableOther = mutableListOf<BuildableOtherInfo>() for(s in listOf(path.first(), path.last())){ for(j in -2 until 3) { for (k in -2 until 3) { if (abs(j) + abs(k) <= 2) { val x = s.x + j val y = s.y + k if (x in 0 until N && y in 0 until N) { for (cc in coordInfo[x][y]) { val (index, other) = cc if (i != index) { buildableOther.add(BuildableOtherInfo(index, other)) } } } } } } } result[i] = CalcResult(i, path, buildTurn, benefitPerTurn, buildableMinMoney, 0, benefitToLastTurn, buildableOther) } return result } fun convertPathToString(path: List<Coord>): List<String> { val result = mutableListOf<String>() for (i in path.indices) { if (i == 0) { result.add("0 ${path[i].x} ${path[i].y}") continue } if (i == path.lastIndex) { result.add("0 ${path[i].x} ${path[i].y}") continue } val from = path[i - 1] val now = path[i] val to = path[i + 1] if (abs(now.y - from.y) == 1 && abs(to.y - now.y) == 1) { result.add("1 ${now.x} ${now.y}") } else if (abs(now.x - from.x) == 1 && abs(to.x - now.x) == 1) { result.add("2 ${now.x} ${now.y}") } else if (now.y - from.y == 1 && to.x - now.x == 1) { result.add("3 ${now.x} ${now.y}") } else if (now.x - from.x == -1 && to.y - now.y == -1) { result.add("3 ${now.x} ${now.y}") } else if (now.y - from.y == 1 && to.x - now.x == -1) { result.add("4 ${now.x} ${now.y}") } else if (now.x - from.x == 1 && to.y - now.y == -1) { result.add("4 ${now.x} ${now.y}") } else if (now.x - from.x == 1 && to.y - now.y == 1) { result.add("5 ${now.x} ${now.y}") } else if (now.y - from.y == -1 && to.x - now.x == -1) { result.add("5 ${now.x} ${now.y}") } else if (now.x - from.x == -1 && to.y - now.y == 1) { result.add("6 ${now.x} ${now.y}") } else if (now.y - from.y == -1 && to.x - now.x == 1) { result.add("6 ${now.x} ${now.y}") } } return result } fun solveLocal() { val resourcePath = Paths.get("ahc043", "src", "main", "resources") val inDir = Paths.get(resourcePath.toString(), "in").toFile() val outDir = Paths.get(resourcePath.toString(), "out").toFile() if (!outDir.exists()) { outDir.mkdirs() } val resultFile = Paths.get(resourcePath.toString(), "result.csv").toFile() val resultFileWriter = FileWriter(resultFile, false) resultFileWriter.write("File, Score\n") resultFileWriter.flush() if (inDir.listFiles() != null) { for (inFile in inDir.listFiles()!!.sortedBy { it.name }) { val fileName = inFile.name val outFile = File(Paths.get(outDir.toString(), fileName).toString()) if (!outFile.exists()) { outFile.createNewFile() } System.setIn(FileInputStream(inFile)) System.setOut(PrintStream(outFile)) val startTime = System.currentTimeMillis() solve() val endTime = System.currentTimeMillis() PrintStream(FileOutputStream(FileDescriptor.out)).println("solve end $fileName ${endTime - startTime}ms") } for (inFile in inDir.listFiles()!!.sortedBy { it.name }) { val fileName = inFile.name val outFile = File(Paths.get(outDir.toString(), fileName).toString()) val pb = ProcessBuilder("cmd", "/c", ".\\vis.exe", ".\\in\\${inFile.name}", ".\\out\\${outFile.name}") pb.directory(resourcePath.toFile()) pb.redirectErrorStream(true) val process = pb.start() process.waitFor() val br = BufferedReader(InputStreamReader(process.inputStream)) val scoreString = br.readLine() br.close() PrintStream(FileOutputStream(FileDescriptor.out)).println(scoreString) val score = scoreString.split(" ")[2].toInt() resultFileWriter.write("$fileName, $score\n") resultFileWriter.flush() } } } fun main(args: Array<String>) { val env = if (args.isNotEmpty() && args[0] == "local") { Env.Local } else { Env.Prod } if (env == Env.Local) { solveLocal() } else { solve() } }