5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
题目链接:http://lx.lanqiao.org/problem.page?gpid=T16
题目分析:题意雷死人了,这个人每天要回到预设起点,也就是说对于除了起点以外的每个点都要遍历它们邻接边个数次,则我们可以将每个点的权值设定为边长*2+边两端点的点权,一开始在起点则起点多谈话一次,因为对于同一棵生成树除了起点外的所有将要遍历的点权边权和是个固定的值,因此只需要取点权最小的点做起点,直接求最小生成树
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const INF = 0x3fffffff;
int const MAXN = 1e4 + 5;
int const MAXM = 1e5 + 5;
int fa[MAXN];
int a[MAXN];
int n, m, ans;
struct EDGE
{
int x, y;
int val;
}e[MAXM];
bool cmp(EDGE a, EDGE b)
{
return a.val < b.val;
}
void UF_set()
{
for(int i = 0; i < MAXN; i++)
fa[i] = i;
}
int Find(int x)
{
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
void Union(int a, int b)
{
int r1 = Find(a);
int r2 = Find(b);
if(r1 != r2)
fa[r2] = r1;
}
void Kruskal()
{
UF_set();
int cnt = 0;
for(int i = 0; i < m; i++)
{
int x = e[i].x;
int y = e[i].y;
if(Find(x) != Find(y))
{
Union(x, y);
ans += e[i].val;
cnt ++;
}
if(cnt >= n)
break;
}
}
int main()
{
ans = 0;
int mi = INF;
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
mi = min(mi, a[i]);
}
for(int i = 0; i < m; i++)
{
scanf("%d %d %d", &e[i].x, &e[i].y, &e[i].val);
e[i].val = e[i].val * 2 + a[e[i].x - 1] + a[e[i].y - 1];
}
sort(e, e + m, cmp);
Kruskal();
printf("%d\n", ans + mi);
}原文地址:http://blog.csdn.net/tc_to_top/article/details/44926075