您的位置:首页 > 其它

[NOIp 2017]宝藏

2017-12-17 14:02 417 查看

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)$。

//Is is made by Awson on 2017.12.17
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = 12;
const int SIZE = 1<<12;
const int INF = ~0u>>1;

int n, m, u, v, c, U;
int g[N+5][N+5];
LL sval[N+5][SIZE+5], qval[SIZE+5][SIZE+5], f[N+5][SIZE+5];

void work() {
scanf("%d%d", &n, &m); U = (1<<n)-1;
memset(g, 127, sizeof(g));
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &u, &v, &c);
g[u][v] = g[v][u] = Min(c, g[u][v]);
}
memset(sval, 127/3, sizeof(sval));
for (int u = 1; u <= n; u++)
for (int i = 0; i <= U; i++)
for (int v = 1; v <= n; v++)
if ((1<<v-1)&i) sval[u][i] = Min(sval[u][i], 1ll*g[u][v]);
memset(qval, 127/3, sizeof(qval));
for (int i = 0; i <= U; i++) {
int C = i^U;
for (int j = C; j; j = (j-1)&C) {
LL cnt = 0;
for (int u = 1; u <= n; u++)
if ((1<<u-1)&j) cnt += sval[u][i];
qval[j][i] = Min(qval[j][i], cnt);
}
}
LL ans = 1ll*INF;
for (int root = 1; root <= n; root++) {
memset(f, 127/3, sizeof(f)); LL inf = f[0][0];
f[0][1<<root-1] = 0;
for (int dep = 0; dep < n; dep++)
for (int i = 0; i <= U; i++) if (f[dep][i] != inf) {
int C = i^U;
for (int j = C; j; j = (j-1)&C)
f[dep+1][i|j] = Min(f[dep+1][i|j], f[dep][i]+qval[j][i]*(dep+1));
}
for (int i = 0; i < n; i++) ans = Min(ans, f[i][U]);
}
printf("%lld\n", ans);
}
int main() {
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: