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()); }