题意:n个点,m条边,求次小生成树
思路:求生成树任意两个点间的线段最大值.跑一遍最最小生成树,最小花费为ans1,那么ans2=min(ans1+cost[i][j]-path[i][j])就是去掉生成树上某一条边,再补一条
#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N=1000+5;
const int MOD=1e9+7;
const int INF=0x3f3f3f3f;
int a[MAX_N],cost[MAX_N][MAX_N],vis[MAX_N],path[MAX_N][MAX_N],mincost[MAX_N],pre[MAX_N],used[MAX_N][MAX_N];
int n,m,ans1,ans2;
void prim(){
for(int i=2;i<=n;i++) mincost[i]=INF;
mincost[1]=0;
for(int i=1;i<=n;i++){
int mn=INF,v=-1;
for(int j=1;j<=n;j++)
if(!vis[j]&&mincost[j]<mn) mn=mincost[j],v=j;
// cout <<"v="<<v<<endl;
if(pre[v]!=-1){
used[v][pre[v]]=used[pre[v]][v]=1;
// printf("%d %d no\n",v,pre[v]);
for(int u=1;u<=n;u++){
if(vis[u]){
path[u][v]=path[v][u]=max(cost[v][pre[v]],path[u][pre[v]]);
}
}
}
ans1+=mincost[v];
vis[v]=1;
for(int j=1;j<=n;j++){
if(!vis[j]&&cost[v][j]<mincost[j]){
// printf("~~~%d %d\n",v,j);
pre[j]=v;
mincost[j]=cost[v][j];
}
}
}
}
void find_second(){
ans2=INF;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!used[i][j]){
// if(ans1-path[i][j]+cost[i][j]<ans2)
// printf("!!!!%d %d\n",i,j);
ans2=min(ans2,ans1-path[i][j]+cost[i][j]);
}
}
}
}
int main(void){
int t;cin >> t;
while(t--){
ans1=ans2=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cost[i][j]=INF;
memset(vis,0,sizeof vis);
memset(pre,-1,sizeof pre);
memset(used,0,sizeof used);
memset(path,0,sizeof path);
for(int i=1;i<=m;i++){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
cost[u][v]=cost[v][u]=c;
}
// cout <<cost[1][3]<<endl;
prim();
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++){
// printf("path[%d][%d]=%d\n",i,j,path[i][j]);
// }puts("");
// }
find_second();
printf("%d %d\n",ans1,ans2);
}
}