zoukankan      html  css  js  c++  java
  • [bzoj3754]Tree之最小方差树【暴力】【MST】

    【题目描述】

    Description

    Wayne在玩儿一个很有趣的游戏。在游戏中,Wayne建造了N个城市,现在他想在这些城市间修一些公路,当然并不是任意两个城市间都能修,为了道路系统的美观,一共只有M对城市间能修公路,即有若干三元组 (Ui,Vi,Ci)表示Ui和Vi间有一条长度为Ci的双向道路。当然,游戏保证了,若所有道路都修建,那么任意两城市可以互相到达。Wayne拥有恰好N-1支修建队,每支队伍能且仅能修一条道路。当然,修建长度越大,修建的劳累度也越高,游戏设定是修建长度为C的公路就会有C的劳累度。当所有的队伍完工后,整个城市群必须连通,而这些修建队伍们会看看其他队伍的劳累情况,若劳累情况差异过大,可能就会引发骚动,不利于社会和谐发展。Wayne对这个问题非常头疼,于是他想知道,这N1支队伍劳累度的标准差最小能有多少。
    标准差的定为:设有N个数,分别为ai,它们的平均数为 ,那么标准差就是

    Input

    第一行两个正整数N,M
    接下来M行,每行三个正整数Ui,Vi,Ci

    Output

    输出最小的标准差,保留四位小数。

    Sample Input

    3 3
    1 2 1
    2 3 2
    3 1 3

    Sample Output

    0.5000

    HINT

    N<=100,M<=2000,Ci<=100

    Source

    【题解】

     把标准差看成方差Wa了无数发。。

    对于任意一种选取方案,若把 a的平均值 看做自变量x,暴力展开可知:当 x==a的平均值 时,标准差取到最小值。

    因此可以枚举 a的平均值 。

    若每隔 1/(n-1) 枚举一次, 复杂度太高无法通过。

    但由于 a 为整数,所以对于两条边权值分别为 ax, ay(ax<ay), 当枚举 [ax..mid) 或 (mid..r] 时,选取方案始终相同。

    因此只要枚举在两边的情况和中间的情况。

    具体来说,只要每隔0.25枚举一次。

    /* --------------
        user Vanisher
        problem bzoj-3754 
    ----------------*/
    # include <bits/stdc++.h>
    # define 	ll 		long long
    # define 	M 		2010
    # define 	N 		110
    using namespace std;
    int read(){
    	int tmp=0, fh=1; char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    	return tmp*fh;
    }
    struct node{
    	int u,v,w;
    	double vote;
    }e[M];
    int f[N],n,m;
    int dad(int x){
    	if (f[x]==x) return x;
    		else return f[x]=dad(f[x]);
    }
    double sqr(double x){
    	return x*x;
    }
    bool cmp(node x, node y){
    	return x.vote<y.vote;
    }
    double check(double x){
    	for (int i=1; i<=m; i++)
    		e[i].vote=sqr(e[i].w-x);
    	sort(e+1,e+m+1,cmp);
    	for (int i=1; i<=n; i++) f[i]=i;
    	int num=n,i=0;
    	double sum=0,now;
    	while (num>1){
    		i++; int u=dad(e[i].u), v=dad(e[i].v);
    		if (u!=v){
    			num--;
    			sum=sum+e[i].w;
    			f[u]=v;
    		}
    	}
    	now=sum/(n-1); sum=0; num=n;
    	for (int i=1; i<=n; i++) f[i]=i;
    	i=0;
    	while (num>1){
    		i++; int u=dad(e[i].u), v=dad(e[i].v);
    		if (u!=v){
    			num--;
    			sum=sum+sqr(e[i].w-now);
    			f[u]=v;
    		}
    	}
    	return sqrt(sum/(n-1));
    }
    int main(){
    	n=read(), m=read(); 
    	double now=0,ans,ansnow;
    	for (int i=1; i<=m; i++){
    		e[i].u=read(), e[i].v=read(), e[i].w=read();
    		now=now+e[i].w;
    	} 
    	now=now/m,ans=ansnow=check(now);
    /*	for (double T=30; T>0.1; T=T*0.9998){
    		int fh=rand()%2; if (fh==0) fh--;
    		double tmp=rand()*T/32767*fh+now,anstmp=check(tmp);
    		ans=min(ans,anstmp);
    		if (anstmp<ansnow)
    			now=tmp, ansnow=anstmp;
    			else if (exp((ansnow-anstmp)/T)>rand()*1.0/32767)
    				now=tmp, ansnow=anstmp;
    	}*/
    	double eps=1.0/(m-1);
    	for (double T=0; T<=100; T+=0.25)
    		ans=min(ans,check(T));
    	printf("%.4lf
    ",ans);
    	return 0;
    }
    
     一开始写的退火,知识水平不够根本过不去。。。

  • 相关阅读:
    在ubuntu 12.04 x64下编译hadoop2.4
    Learn ZYNQ (9)
    Learn ZYNQ (8)
    Jquery 中 ajaxSubmit使用讲解(转)
    JSON.parse()和JSON.stringify()的区别
    $('div','li'),$('div , li'),$('div li')的区别
    用正则表达式来去除字符的前后空格
    git add 命令添加所有改动内容
    js基础知识
    Web开发学习笔记
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136027.html
Copyright © 2011-2022 走看看