bzoj 3143 [Hnoi2013]游走

题目大意

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

分析

直接搞边比较困难
我们考虑到如果一条边期望经过次数多,就应该给它一个小的编号
而一条边(x,y)期望经过次数就是(frac {e[x]} {du[x]}+frac {e[y]} {du[y]})
e[x]用高斯消元求

方法1

设e[i]为i期望有多少次机会往旁边走
注意到:走到n后无法走回来
所以即使有连边i-n,i也无法从n转移过来
(e[n]=0)
(e[1]=(sum frac {e[y]} {du[y]} )+1)(每种方案一都多走一次)
(e[x]=sum frac {e[y]} {du[y]} (x!=1))

方法2

设e[i]为i期望经过多少次
注意到:走到n后无法走回来
(e[n]=sum frac {e[y]} {du[y]}=1)
(e[x]=sum frac {e[y]} {du[y]})
最后算边的次数时注意不能从n走来

solution1

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long double db;
const int M=507;
const db eps=1e-10;

inline int rd(){
	int x=0;bool f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
	for(;isdigit(c);c=getchar()) x=x*10+c-48;
	return f?x:-x;
}

int n,m;
int du[M];

struct node{
	int x,y;
	db z;
	node(int xx=0,int yy=0,db zz=0){
		x=xx;
		y=yy;
	}
}ed[M*M*2];

struct Guass{
	db a[M][M];
	db res[M];
	int n,m;
	void init(int nn){
		n=nn;
		m=nn+1;
		memset(res,0,sizeof(res));
		memset(a,0,sizeof(a));
	}
	void xiao(int x,db ff,int y){
		for(int i=1;i<=m;i++)
			a[y][i]-=a[x][i]*ff;
	}
	void getres(){
		int i,j;
		db tp;
		for(i=n;i>0;i--){
			tp=0;
			for(j=i+1;j<m;j++) tp+=a[i][j]*res[j];
			res[i]=(-tp-a[i][m])/a[i][i];
		}
	}
	void gauss(){
		int i,j,k=0;
		for(i=1;i<m;i++){
			k++;
			for(j=k;j<=n;j++)
			if(fabs(a[j][i])>eps){
				swap(a[j],a[k]);
				break;
			}
			for(j=1;j<=n;j++)
			if(fabs(a[j][i])>eps&&j!=k)
				xiao(k,a[j][i]/a[k][i],j);
		}
		getres();
	}
}GS;

bool cmpw(node x,node y){
	return x.z>y.z;
}

int main(){

	n=rd(),m=rd();
	int i,x,y;
	for(i=1;i<=m;i++){
		x=rd(),y=rd();
		du[x]++; du[y]++;
		ed[i]=node(x,y);
	}
	GS.init(n);
	for(i=1;i<=m;i++){
		x=ed[i].x, y=ed[i].y;
		if(x!=n) GS.a[x][y]+=1.0/du[y];
		if(y!=n) GS.a[y][x]+=1.0/du[x];
	}
	for(i=1;i<=n;i++) GS.a[i][i]-=1.0;
	GS.a[1][n+1]+=1.0;
	GS.gauss();

	for(i=1;i<=m;i++){
		x=ed[i].x, y=ed[i].y;
		ed[i].z=0;
		ed[i].z+=GS.res[x]/du[x];
		ed[i].z+=GS.res[y]/du[y];
	}
	
	sort(ed+1,ed+m+1,cmpw);
	db ans=0;
	for(i=1;i<=m;i++) ans+=ed[i].z*i;

	printf("%.3Lf",ans+eps);	

	return 0;
}

solution2

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long double db;
const int M=507;

inline int rd(){
	int x=0;bool f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
	for(;isdigit(c);c=getchar()) x=x*10+c-48;
	return f?x:-x;
}

int n,m;
int du[M];

struct node{
	int x,y;
	db z;
	node(int xx=0,int yy=0,db zz=0){
		x=xx;
		y=yy;
	}
}ed[M*M*2];

struct Gauss{
	db a[M][M];
	db res[M];
	int n,m;
	void init(int nn){
		n=nn;
		m=nn+1;
		memset(res,0,sizeof(res));
		memset(a,0,sizeof(a));
	}
	void xiao(int x,db ff,int y){
		for(int i=1;i<=m;i++)
			a[y][i]-=a[x][i]*ff;
	}
	void getres(){
		int i,j;
		db tp;
		res[m]=1;
		for(i=n;i>0;i--){
			tp=0;
			for(j=i+1;j<=m;j++) tp+=a[i][j]*res[j];
			res[i]=-tp/a[i][i];
		}
		res[m]=0;//****
	}
	void gauss(){
		int i,j,k=0;
		for(i=1;i<m;i++){
			k++;
			for(j=k;j<=n;j++)
			if(a[j][i]){
				swap(a[j],a[k]);
				break;
			}
			for(j=1;j<=n;j++)
			if(a[j][i]&&j!=k)
				xiao(k,a[j][i]/a[k][i],j);
		}
		getres();
	}
}GS;

bool cmpw(node x,node y){
	return x.z>y.z;
}

int main(){
	
	n=rd(),m=rd();
	int i,x,y;
	for(i=1;i<=m;i++){
		x=rd(),y=rd();
		du[x]++; du[y]++;
		ed[i]=node(x,y);
	}
	GS.init(n-1);//只用解1...n-1,下面的代码中-1都只是将第一条方程去掉后每条方程往上移一下而已
	for(i=1;i<=m;i++){
		x=ed[i].x, y=ed[i].y;
		if(y!=n) GS.a[x-1][y]+=1.0/du[y];
		if(x!=n) GS.a[y-1][x]+=1.0/du[x];
	}
	for(i=2;i<n;i++) GS.a[i-1][i]-=1.0;
	GS.a[n-1][n]-=1.0;//第n项为常数项 
	GS.gauss();
	GS.res[n]=0;
	
	for(i=1;i<=m;i++){
		x=ed[i].x, y=ed[i].y;
		ed[i].z=0;
		ed[i].z+=GS.res[x]/du[x];
		ed[i].z+=GS.res[y]/du[y];
	}
	
	sort(ed+1,ed+m+1,cmpw);
	db ans=0;
	for(i=1;i<=m;i++) ans+=ed[i].z*i;

	printf("%.3Lf
",ans);	

	return 0;
}
原文地址:https://www.cnblogs.com/acha/p/6419890.html