小记: 最开始是先将边求出最小生成树,然后再将点的值加进来, 提交上去,20分, 想不到错在哪,或者说,不想想,于是百度了一下,顿悟了。必须将边权值进行计算出来后,再进行最小生成树的求解,因为,如果直接对原树求最小生成树,那么可能有两个节点的权值很大,但是有一个节点的值很小,而两个大的的节点的节点连得边权值要小,而其中一个节点和那个节点权值小的点的连边 的边权值却有蛮大,不过小的节点的权值和两个节点权值大的其中一个节点的权值再加上两者的连边的权值的三者值的和小于两个权值大的两个节点的权值与两者的连边的权值的三者和,这样的话,结果就是错的。
思路:克鲁斯卡尔,因为边数大,所以普利姆就pass掉了。对每条边,其边权值是两个节点的权值与两者的连边的权值三者的总和。最后卡鲁斯卡尔求最小生成树,整棵树的权值再加上最小的点权值就是answer了
代码;
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define MAX_ 100100
#define MAX 0x7fffffff
#define max(a,b) ((a>b)?(a):(b))
struct point {
int u,v,cap;
} edge[MAX_*10];
bool cmp(const point a,const point b){
return a.cap < b.cap;
}
//int head[MAX_];
int d[MAX_], p[MAX_];
//bool vis[MAX_];
int fa[MAX_];
int M, n, m;
void add(int from, int to, int cap) {
edge[M].u = from;
edge[M].v = to;
edge[M].cap = cap * 2 + p[from] + p[to];
//edge[M].next = head[from];
//head[from] = M++;
M++;
}
int find(int x){
if(fa[x] == x)return fa[x];
return fa[x] = find(fa[x]);
}
void kruscal(){
for(int i = 1; i <= n; ++i){
fa[i] = i;
d[i] = 0;
}
int u, v, fu,fv, sum = 0;
for(int i = 0; i < M; ++i){
u = edge[i].u;
v = edge[i].v;
fu = find(u);
fv = find(v);
if(fu == fv)continue;
//printf(": %d\n",edge[i].cap);
sum += edge[i].cap;
//uni(u,v);
fa[fu] = fv;
d[u] ++;
d[v] ++;
}
//printf("sum = %d\n",sum);
//sum <<= 1;
int tmp = MAX;
for(int i = 1; i <= n; ++i){
//sum += d[i] * p[i];
if(tmp > p[i])tmp = p[i];
}
printf("%d\n",sum + tmp);
}
int main() {
int i;
int s,t,c;
while(~scanf("%d%d",&n,&m)) {
M = 0;
//memset(head,-1,sizeof(head));
for(i = 1; i <= n; ++i ){
scanf("%d",&p[i]);
}
for(i = 0; i < m; i++) {
scanf("%d%d%d",&s,&t,&c);
add(s,t,c);
//add(t,s,c);
}
sort(edge,edge + M,cmp);
/*for(i = 0; i < M; ++i){
printf("%d---%d : %d\n",edge[i].u,edge[i].v,edge[i].cap);
}*/
kruscal();
}
return 0;
}