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

    (color{#0066ff}{ 题目描述 })

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

    (color{#0066ff}{输入格式})

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

    (color{#0066ff}{输出格式})

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

    (color{#0066ff}{输入样例})

    3 3
    2 3
    1 2
    1 3
    

    (color{#0066ff}{输出样例})

    3.333
    

    (color{#0066ff}{数据范围与提示})

    边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

    (color{#0066ff}{题解})

    根据期望等于概率*权值,权值我们到时候分配, 先考虑概率

    直接考虑边的概率我们不好分析,但如果我们知道点的概率

    (f[i])为从1走到i的概率,那么显然一条边(x- y)的概率就是(frac{f[x]}{du[x]}+frac{f[y]}{du[y]})

    考虑f怎么弄出来

    其实就是跟当前点直接相连的所有点的(f)值*那个点的(frac 1 {du})

    而且,1的f值在最后要+1, 因为刚开始就在1

    所以这个东西可以用高斯消元求解, 然后转到对边的贡献

    注意1和n要特判!!

    下面跑一下样例

    (f[1]=frac 1 2 f[2]+1)

    (f[2]=frac 1 2 f[1])(到了n便不能再走)

    (f[3]=frac 1 2 f[2]+frac 1 2 f[1])

    通过高斯消元,解出来

    (f[1]=frac 4 3 ,f[2]=frac 2 3, f[3]=1)

    (边1-2,ans=frac 1 2 f[1]+frac 1 2 f[2]=1)

    (边1-3,ans=frac 1 2 f[1]=frac 2 3)

    (边2-3,ans=frac 1 2 f[2]=frac 1 3)

    于是(ans=frac 1 3 * 3 + frac 2 3 * 2 + 1 * 1=frac 4 3 + 2=3.333)

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const double eps = 1e-10;
    const int maxn = 520;
    struct node {
    	int to;
    	node *nxt;
    	node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
    };
    node *head[maxn];
    double a[maxn][maxn], ans[maxn];
    int n, m, du[maxn];
    std::priority_queue<double> q;
    std::vector<std::pair<int, int> > v;
    void add(int from, int to) {
    	head[from] = new node(to, head[from]);
    }
    void gaosi() {
    	for(int i = 1; i <= n; i++) {
    		int pos = i;
    		for(int j = i; j <= n; j++) if(fabs(a[j][i]) - fabs(a[pos][i]) >= eps) pos = j;
    		if(pos != i) for(int j = 1; j <= n + 1; j++) std::swap(a[pos][j], a[i][j]);
    		for(int j = i + 1; j <= n + 1; j++) a[i][j] /= a[i][i];
    		a[i][i] = 1;
    		for(int j = 1; j <= n; j++) {
    			if(i == j) continue;
    			double now = a[j][i];
    			for(int k = i; k <= n + 1; k++) a[j][k] -= now * a[i][k];
    		}
    	}
    	for(int i = 1; i <= n; i++) ans[i] = a[i][n + 1];
    }
    int main() {
    	n = in(), m = in();
    	int x, y;
    	for(int i = 1; i <= m; i++) {
    		du[x = in()]++, du[y = in()]++;
    		add(x, y), add(y, x);
    		v.push_back(std::make_pair(x, y));
    	}
    	a[1][n + 1] -= 1;
    	for(int i = 1; i <= n; i++) {
    		a[i][i] -= 1;
    		for(node *o = head[i]; o; o = o->nxt) {
    			if(o->to == n) continue;
    			a[i][o->to] += 1.0 / (double)du[o->to];
    		}
    	}
    	gaosi();
    	for(int i = 0; i < (int)v.size(); i++) {
    		double now = 0;
    		x = v[i].first, y = v[i].second;
    		if(x != n) now += 1.0 / (double)du[x] * ans[x];
    		if(y != n) now += 1.0 / (double)du[y] * ans[y];
    		q.push(now);
    	}
    	double tot = 0;
    	for(int i = 1; i <= m; i++) tot += (double)i * q.top(), q.pop();
    	printf("%.3f", tot);
    	return 0;
    }
    
  • 相关阅读:
    vnode之update 还是没太懂
    vnodec创建之标签
    1054 求平均值
    1053 住房空置率
    1052 卖个萌
    1051 复数乘法
    1050 螺旋矩阵
    1049 数列的片段和
    1048 数字加密
    1047 编程团体赛
  • 原文地址:https://www.cnblogs.com/olinr/p/10415013.html
Copyright © 2011-2022 走看看