拿到这个题第一反应想到了wikioi里的“舒适的路线”,便想到了最最暴力的解法:枚举每个点,从此点开始每次都做一遍最小生成树,然后按以下程序判断
(1)是否连通 (2)边数是否小于当前记录的最小边数 (3)最大价值是否最小
于是便得到了下面的代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define INF (1<<30)
#define MAXN 310
#define MAXM 100000
using namespace std;
int f[MAXN];
int x[MAXM],y[MAXM],r[MAXM],w[MAXM];
int mem[MAXM];
int cmp(const int a,const int b){
return w[a]<w[b];
}
int getf(int x){
return f[x]==x ? x : f[x]=getf(f[x]);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
x[i]=u;
y[i]=v;
w[i]=c;
r[i]=i;
}
sort(r+1,r+m+1,cmp);
int cntm=INF;
int valm=INF;
for(int j=1;j<m;j++){
int cnt=0;
int val=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++) mem[i]=1;
bool flag=false;
for(int i=j;i<=m;i++){
int p=getf(x[r[i]]),q=getf(y[r[i]]);
if(p!=q){
f[p]=q;
cnt++;
val=max(w[r[i]],val);
mem[q]+=mem[p];
if(mem[q]==n){
flag=true;
break;
}
}
}
if(flag) {
if(cnt<cntm){
cntm=cnt;
valm=val;
}
if(cnt==cntm)
valm=min(val,valm);
}
}
printf("%d %d",cntm,valm);
return 0;
}
时间是费得长了点,幸而过了。
然后在网上搜题解,看到了“二分”这个神方法。基本思想是枚举答案,看在此答案下是否符合题意,就得到了如下代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define INF (1<<30)
#define MAXN 1010
#define MAXM 100000
using namespace std;
int f[MAXN];
int n,m;
struct node{
int a,b,c;
}e[MAXM];
bool cmp(const node &h,const node &k){
return h.c<k.c;
}
int getf(int x){
return f[x]==x ? x : f[x]=getf(f[x]);
}
bool getans(int val){
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
if(val>=e[i].c){
int p=getf(e[i].a),q=getf(e[i].b);
if(p!=q) f[p]=q;
}
for(int i=2;i<=n;i++) if(getf(1)!=getf(i)) return false;
return true;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
}
sort(e+1,e+m+1,cmp);
int left=1;
int right=m;
int ans;
while(left<=right){
int mid=(left+right) >> 1;
if(getans(e[mid].c)){
right=mid-1;
ans=e[mid].c;
}
else left=mid+1;
}
printf("%d %d\n",n-1,ans);
return 0;
}