zoukankan      html  css  js  c++  java
  • [bzoj3143] [HNOI2013]游走

    Description

    一个无向连通图,顶点从1编号到N,边从1编号到M。
    小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
    现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

    Input

    第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。

    Output

    仅包含一个实数,表示最小的期望值,保留3位小数。

    Sample Input

    3  3                
    2  3
    1  2
    1  3
    

    Sample Output

    3.333
    

    Solution

    问题可以转化为求每条边期望被经过多少次,然后期望大的编号小就行了。

    对于每条边,它连接的两个点每次有一定概率走这条边,所以这条边的期望就是:

    [frac{E(u)}{deg(u)}+frac{E(v)}{deg(v)} ]

    所以问题转化为求一个点期望被经过多少次。

    (E(x))表示这个点期望经过次数,可以列出式子:

    [E(x)=sum_{(x,v)in Edge} frac{E(v)}{deg(v)} ]

    然后列方程解出来就行了。

    对于最后一个点,走到它就结束了,所以忽略(n)号点连出来的边。

    同样,算边的期望时也忽略。

    对于第一个点,期望应为:

    [E(1)=sum_{(1,v)in Edge}frac{E(v)}{deg(v)}+1 ]

    因为一开始就经过了一次。

    #include<bits/stdc++.h>
    using namespace std;
    
    #define ONLINE_JUDGE
    
    #ifdef ONLINE_JUDGE
    #define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2)?EOF:*p1++)
    #endif
    
    namespace fast_IO {
    	char buf[1<<21],*p1=buf,*p2=buf;
    
    	template <typename T> inline void read(T &x) {
    		x=0;T f=1;char ch=getchar();
    		for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    		for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    	}
    	template <typename T,typename... Args> inline void read(T& x,Args& ...args) {
    		read(x),read(args...);
    	}
    
    	char buf2[1<<21],a[80];int p,p3=-1;
    
    	inline void flush() {fwrite(buf2,1,p3+1,stdout),p3=-1;}
    	template <typename T> inline void write(T x) {
    		if(p3>(1<<20)) flush();
    		if(x<0) buf2[++p3]='-',x=-x;
    		do {a[++p]=x%10+48;} while(x/=10);
    		do {buf2[++p3]=a[p];} while(--p);
    		buf2[++p3]='
    ';
    	}
    	template <typename T,typename... Args> inline void write(T x,Args ...args) {
    		write(x),write(args...);
    	}
    }
    
    using fast_IO :: read;
    using fast_IO :: write;
    using fast_IO :: flush;
    
    #define lf double 
    
    const lf eps = 1e-5;
    const int maxn = 5e2+10;
    
    lf f[maxn][maxn],w[maxn*maxn];
    int n,m,head[maxn],tot,deg[maxn];
    struct edge{int to,nxt;}e[maxn*maxn];
    
    void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
    void ins(int u,int v) {add(u,v),add(v,u);}
    
    void gauss() {
    	for(int i=1;i<=n;i++) {
    		if(fabs(f[i][i])<eps) 
    			for(int j=i;j<=n;j++)
    				if(fabs(f[j][i])>eps) {
    					for(int k=1;k<=n+1;k++)
    						swap(f[i][k],f[j][k]);
    					break;
    				}
    		for(int j=i+1;j<=n;j++) {
    			lf t=f[j][i]/f[i][i];
    			for(int k=i;k<=n+1;k++) f[j][k]=f[j][k]-f[i][k]*t;
    		}
    	}
    	for(int i=n;i;i--) {
    		lf tmp=f[i][n+1];
    		for(int j=i+1;j<=n;j++) tmp-=f[j][j]*f[i][j];
    		tmp/=f[i][i];f[i][i]=tmp;
    	}
    }
    
    int main() {
    	read(n,m);
    	for(int i=1,x,y;i<=m;i++) read(x,y),ins(x,y),deg[x]++,deg[y]++;
    	for(int x=1;x<=n;x++) {
    		for(int i=head[x];i;i=e[i].nxt)
    			if(e[i].to!=n) f[x][e[i].to]-=1.0/deg[e[i].to];
    		f[x][x]+=1.0;
    	}f[1][n+1]+=1.0;
    	gauss();f[n][n]=0;
    	for(int i=1;i<=tot;i++) 
    		w[(i+(i&1))>>1]+=f[e[i].to][e[i].to]/(lf)deg[e[i].to];
    	sort(w+1,w+tot/2+1);lf ans=0;
    	for(int i=1;i<=tot/2;i++) ans+=w[i]*(lf)(tot/2-i+1);
    	printf("%.3lf
    ",ans);
    	flush();
    	return 0;
    }
    
  • 相关阅读:
    STL map用法总结(multimap)
    Ice Cream Tower
    位运算 进制转化 STL中bitset用法
    《算法导论》插入排序
    C++输入/输出流
    kuangbin大佬模板(侵删)- hdu 2222
    poj 3461
    动态规划入门-01背包问题
    Dropping water balloons (入门dp)
    TSP
  • 原文地址:https://www.cnblogs.com/hbyer/p/10196593.html
Copyright © 2011-2022 走看看