您的位置:首页 > 其它

【枚举】【最小生成树】【kruscal】bzoj3754 Tree之最小方差树

2014-11-25 12:19 381 查看
发现,若使方差最小,则使Σ(wi-平均数)2最小即可。

因为权值的范围很小,所以我们可以枚举这个平均数,每次把边权赋成(wi-平均数)2,做kruscal。

但是,我们怎么知道枚举出来的平均数是不是恰好是我们的这n-1条边的呢? 就在更新答案的时候加个特判就行了。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define N 101
#define M 2001
typedef double db;
int fa
,a[M],n,m,minv,maxv,rank
;
db ans=999999999999999999999999999999.0;
bool cmp(const int &a,const int &b){return a>b;}
struct Edge{int u,v,w;db fw;void Read(){scanf("%d%d%d",&u,&v,&w);}}edges[M];
bool operator < (const Edge &a,const Edge &b){return a.fw<b.fw;}
void init()
{
for(int i=1;i<=n;i++) fa[i]=i;
memset(rank,0,sizeof(rank));
}
int findroot(int x)
{
if(x==fa[x]) return x;
int rt=findroot(fa[x]);
fa[x]=rt;
return rt;
}
void Union(const int &U,const int &V)
{
if(rank[U]<rank[V]) fa[U]=V;
else
{
fa[V]=U;
if(rank[U]==rank[V]) ++rank[U];
}
}
double sqr(const double &x){return x*x;}
void kruscal(const int &sum)
{
init(); db BA=(db)sum/(db)(n-1);
int tot=0,all=0; db f_all=0;
for(int i=1;i<=m;++i) edges[i].fw=sqr((db)edges[i].w-BA);
sort(edges+1,edges+m+1);
for(int i=1;i<=m;++i)
{
int f1=findroot(edges[i].u),f2=findroot(edges[i].v);
if(f1!=f2)
{
Union(f1,f2);
all+=edges[i].w;
f_all+=edges[i].fw;
if((++tot)==n-1) break;
}
}
if(all==sum) ans=min(ans,f_all);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i) {edges[i].Read(); a[i]=edges[i].w;}
sort(a+1,a+m+1); for(int i=1;i<n;++i) minv+=a[i];
sort(a+1,a+m+1,cmp); for(int i=1;i<n;++i) maxv+=a[i];
for(int i=minv;i<=maxv;++i) kruscal(i);
printf("%.4f\n",sqrt(ans/(db)(n-1)));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: