結果
| 問題 | No.114 遠い未来 |
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2026-10-23 16:15:15 |
| 言語 | D (dmd 2.113.0) |
| 結果 |
TLE
不安定
|
| 実行時間 | - |
| コード長 | 3,215 bytes |
| 記録 | |
| コンパイル時間 | 4,396 ms |
| コンパイル使用メモリ | 192,304 KB |
| 実行使用メモリ | 55,188 KB |
| 最終ジャッジ日時 | 2026-10-23 16:15:56 |
| 合計ジャッジ時間 | 35,138 ms |
|
ジャッジサーバーID (参考情報) |
judge3_0 / judge1_0 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 19 TLE * 1 -- * 8 |
ソースコード
module main;
// https://kmjp.hatenablog.jp/entry/2014/12/29/0900 より
// claude ai より
import std;
import core.bitop;
// 多次元配列をある値で埋める
void fill(A, T)(ref A a, T value) if (isArray!A)
{
alias E = ElementType!A;
static if (isArray!E) {
foreach (ref e; a)
fill(e, value);
} else {
a[] = value;
}
}
// aとbを比較してbの方が小さいならばaの値をbに更新する
void chMin(T)(ref T a, in T b)
{
if (a > b) a = b;
}
/* UnionFind:素集合系管理の構造体(union by size)
isSame(x, y): x と y が同じ集合にいるか。 計算量はならし O(α(n))
unite(x, y): x と y を同じ集合にする。計算量はならし O(α(n))
treeSize(x): x を含む集合の要素数。
*/
struct UnionFind {
int[] size, parents;
this(int n) // make n trees.
{
size.length = parents.length = n;
foreach (i; 0 .. n)
makeTree(i);
}
void makeTree(int x)
{
parents[x] = x; // the parent of x is x
size[x] = 1;
}
bool isSame(int x, int y)
{
return findRoot(x) == findRoot(y);
}
bool unite(int x, int y)
{
x = findRoot(x);
y = findRoot(y);
if (x == y) return false;
if (size[x] > size[y]) {
parents[y] = x;
size[x] += size[y];
} else {
parents[x] = y;
size[y] += size[x];
}
return true;
}
int findRoot(int x)
{
if (x != parents[x])
parents[x] = findRoot(parents[x]);
return parents[x];
}
int treeSize(int x)
{
return size[findRoot(x)];
}
}
int N, M, T;
int[] A, B, C;
int[] V;
int[][] mat;
bool[] ok;
int[][] OPT;
int first;
int minimumSteinerTree()
{
if (T <= 1) return 0;
OPT = uninitializedArray!(int[][])(1 << T, N);
fill(OPT, 100_000);
foreach (p; 0 .. T)
foreach (q; 0 .. N)
OPT[1 << p][q] = mat[V[p]][q];
foreach (S; 1 .. 1 << T) {
if (!(S & (S - 1))) continue;
auto row = OPT[S];
// Sの最下位ビットを含む側をEとして、(E, S-E) の分割を一度だけ列挙する
int low = S & -S;
int rest = S ^ low;
int sub = rest;
while (true) {
sub = (sub - 1) & rest; // restの真部分集合を降順に列挙(0を含む)
int E = low | sub;
auto a = OPT[E];
auto b = OPT[S ^ E];
foreach (p; 0 .. N)
chMin(row[p], a[p] + b[p]);
if (sub == 0) break;
}
// matは最短距離行列(三角不等式が成立)なので1回の緩和で十分
foreach (p; 0 .. N)
foreach (q; 0 .. N)
chMin(row[p], row[q] + mat[p][q]);
}
return OPT[(1 << T) - 1].reduce!min;
}
void main()
{
// 入力
readln.chomp.formattedRead("%d %d %d", N, M, T);
mat = uninitializedArray!(int[][])(N, N);
fill(mat, 10 ^^ 9);
foreach (x; 0 .. N)
mat[x][x] = 0;
A = new int[](M);
B = new int[](M);
C = new int[](M);
foreach (i; 0 .. M) {
readln.chomp.formattedRead("%d %d %d", A[i], B[i], C[i]);
--A[i], --B[i];
mat[A[i]][B[i]] = C[i];
mat[B[i]][A[i]] = C[i];
}
// ワーシャルフロイド法
foreach (i; 0 .. N)
foreach (x; 0 .. N)
foreach (y; 0 .. N)
chMin(mat[x][y], mat[x][i] + mat[i][y]);
ok = new bool[](N);
long mask = 0;
foreach (_; 0 .. T) {
int x = readln.chomp.to!int - 1;
V ~= x;
ok[x] = true;
mask |= 1L << x;
first = x;
}
// 答えの計算と出力
writeln(minimumSteinerTree());
}