【Ybt Gold Medal Navigation 1-1-6】【luogu P3232】Picture Wandering/Wandering

Figure Wandering / Wandering

Topic link: ybt gold medal navigation 1-1-6 / luogu P3232

Topic

For an infinitely connected graph, a person has to walk from one point to the last point, and each time at a point, he will walk to the point connected to this point with equal probability. The cost is the number of this edge.
Now you need to number the edges so that the total score expectation is the smallest.

Ideas

In this question, we see that the number of edges is large, so we can't find the edges directly.

Then we start from the point.
Let fi f_ifi为第iiThe expected number of passes for i points, then we can find a certain edgeeebased on thisThe expected number of passes of e = ffromdufrom + ftoduto =\dfrac{f_{from}}{du_{from}}+\dfrac{f_{to}}{du_{to}}=d ufromffrom+d utoftoDui du_id ui Is the degree of this point).

Then after you know this, we can be greedy and let the side that expects more passes have a small weight.

Now let's see how to find fi f_ifi.
Then we can get the ff equal to the point it can reachThe f- number is multiplied by the probability of coming over here.
But there are some special places, such as starting point1 11 point, because the probability of going from it to the end must be1 11 , then we have to makef 1 f_1f1Add one to the original basis. The probability of other points going out from other points is 0 00 , so there is no need to add anything.
Butnnn can’t be considered, because it’snnn stopped.

That makes up n − 1 n-1n. 1 equations set, we can use Gaussian elimination solution obtained.

Then just follow the instructions above, and that's it.

Code

#include<cstdio>
#include<algorithm>

using namespace std;

struct node {
    
    
	int from, to, nxt;
}e[500001];
int n, m, x, y, le[501], KK, sz, du[501], maxn;
double f[501][501], line[500001], ans;

void add(int x, int y) {
    
    
	e[++KK] = (node){
    
    x, y, le[x]}; le[x] = KK;
	e[++KK] = (node){
    
    y, x, le[y]}; le[y] = KK;
	du[x]++;
	du[y]++;
}

double abss(double now) {
    
    
	if (now < 0) return -now;
	return now;
}

void gas(int n) {
    
    //高斯消元
	for (int i = 1; i <= n; i++) {
    
    
		maxn = i;
		for (int j = i + 1; j <= n; j++)
			if (abss(f[j][i]) > abss(f[maxn][i]))
				maxn = j;
		
		for (int j = 1; j <= n + 1; j++)
			swap(f[i][j], f[maxn][j]);
		
		for (int j = 1; j <= n; j++)
			if (i != j) {
    
    
				for (int k = i + 1; k <= n + 1; k++)
					f[j][k] = f[j][k] - (f[i][k] * (f[j][i] / f[i][i]));
			}
	}
	
	for (int i = 1; i <= n; i++)
		f[i][n + 1] /= f[i][i];
}

int main() {
    
    
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
    
    
		scanf("%d %d", &x, &y);
		add(x, y);
	}
	
	for (int i = 1; i < n; i++) {
    
    
		f[i][i] = -1.0;//把右边的等于f[i]移过来,就是变成负的了
		for (int j = le[i]; j; j = e[j].nxt) {
    
    
			if (e[j].to == n) continue;
			f[i][e[j].to] = 1.0 / (1.0 * du[e[j].to]);//这条边连着的点有多少的几率会走到这个点
		}
	}
	
	f[1][n] = -1.0;//1点到终点的概率一定是1
	gas(n - 1);
	
	for (int i = 1; i <= m; i ++)//得出经过边的次数
		line[i] = f[e[i << 1].from][n] / (1.0 * du[e[i << 1].from]) + f[e[i << 1].to][n] / (1.0 * du[e[i << 1].to]);
	
	sort(line + 1, line + m + 1);//贪心,让经过次数最多的边的权值尽可能小
	
	for (int i = 1; i <= m; i++)//算出总分
		ans += line[i] * (1.0 * (m - i + 1.0));
	
	printf("%.3lf", ans);
	
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43346722/article/details/112422494