拿到这个题第一反应想到了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; }