[NOIp 2017]宝藏

Description

参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。

小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。

小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。

新开发一条道路的代价是:

$$mathrm{L} imes 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
", ans);
62 }
63 int main() {
64     work();
65     return 0;
66 }
原文地址:https://www.cnblogs.com/NaVi-Awson/p/8051892.html