Description
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。
小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。
在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。
新开发一条道路的代价是:
$$\mathrm{L} \times \mathrm{K}$$
L代表这条道路的长度,K代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。
请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代 价最小,并输出这个最小值。
Input
第一行两个用空格分离的正整数 n 和 m,代表宝藏屋的个数和道路数。
接下来 m 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏 屋的编号(编号为 1~n),和这条道路的长度 v。
Output
输出共一行,一个正整数,表示最小的总代价。
Sample Input1
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1
Sample Output1
4
Sample Explanation1
Sample Input2
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 2
Sample Output2
5
Sample Explanation2
HINT
对于 20%的数据: 保证输入是一棵树,$1 \le n \le 8$,$v \le 5000$ 且所有的 v 都相等。
对于 40%的数据: $1 \le n \le 8$,$0 \le m \le 1000$,$v \le 5000$ 且所有的 v 都相等。
对于 70%的数据: $1 \le n \le 8$,$0 \le m \le 1000$,$v \le 5000$
对于 100%的数据: $1 \le n \le 12$,$0 \le m \le 1000$,$v \le 500000$
题解
很容易想到最后构成的图就是一棵树。
比较容易想到的$70pts$就是用搜索来确定这棵树的形态。
我们先枚举根节点,再搜索来确定其父节点。
正解则是子集$DP$了。
我们考虑到什么状态是无后效性的?记$f_{dep,i}$,表示生成树中最深的点深度为$dep$,选点的状态为$i$的最小花费。
转移的话就是考虑第$dep+1$层需要连接哪些点,即$$f_{dep+1,i|j} = min \{f_{dep,i}+(dep+1)*qval_{j,i}\}$$
其中$j$表示第$dep+1$层连的点,$i∩j = \emptyset$;$qval_{j,i}$表示集合$j$中所有的点连向集合$i$中的点最小花费和。
对于$qval$的值我们可以通过$$qval_{j,i} = \sum_{u∈j} sval_{u,i}$$
计算出,其中$sval_{u,i}$表示点$u$到集合$i$中的点最小距离。
显然$$sval_{u,i} = \min_{v∈i} \{w[u][v]\}$$
值得注意的是,其中会有很多不合法的状况,比如对于转移时$j$集合中的点并不是严格的在第$dep+1$层。但可以证明的是,这种不合法的情况总比最优解差。我们允许这种不合法的情况存在。
时间复杂度$O(3^n*n^2)$。
1 //Is is made by Awson on 2017.12.17 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <cstdio> 9 #include <string> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define Max(a, b) ((a) > (b) ? (a) : (b)) 17 #define Min(a, b) ((a) < (b) ? (a) : (b)) 18 using namespace std; 19 const int N = 12; 20 const int SIZE = 1<<12; 21 const int INF = ~0u>>1; 22 23 int n, m, u, v, c, U; 24 int g[N+5][N+5]; 25 LL sval[N+5][SIZE+5], qval[SIZE+5][SIZE+5], f[N+5][SIZE+5]; 26 27 void work() { 28 scanf("%d%d", &n, &m); U = (1<<n)-1; 29 memset(g, 127, sizeof(g)); 30 for (int i = 1; i <= m; i++) { 31 scanf("%d%d%d", &u, &v, &c); 32 g[u][v] = g[v][u] = Min(c, g[u][v]); 33 } 34 memset(sval, 127/3, sizeof(sval)); 35 for (int u = 1; u <= n; u++) 36 for (int i = 0; i <= U; i++) 37 for (int v = 1; v <= n; v++) 38 if ((1<<v-1)&i) sval[u][i] = Min(sval[u][i], 1ll*g[u][v]); 39 memset(qval, 127/3, sizeof(qval)); 40 for (int i = 0; i <= U; i++) { 41 int C = i^U; 42 for (int j = C; j; j = (j-1)&C) { 43 LL cnt = 0; 44 for (int u = 1; u <= n; u++) 45 if ((1<<u-1)&j) cnt += sval[u][i]; 46 qval[j][i] = Min(qval[j][i], cnt); 47 } 48 } 49 LL ans = 1ll*INF; 50 for (int root = 1; root <= n; root++) { 51 memset(f, 127/3, sizeof(f)); LL inf = f[0][0]; 52 f[0][1<<root-1] = 0; 53 for (int dep = 0; dep < n; dep++) 54 for (int i = 0; i <= U; i++) if (f[dep][i] != inf) { 55 int C = i^U; 56 for (int j = C; j; j = (j-1)&C) 57 f[dep+1][i|j] = Min(f[dep+1][i|j], f[dep][i]+qval[j][i]*(dep+1)); 58 } 59 for (int i = 0; i < n; i++) ans = Min(ans, f[i][U]); 60 } 61 printf("%lld\n", ans); 62 } 63 int main() { 64 work(); 65 return 0; 66 }