vijos P1966 夜夜的旅游计划

背景

夜夜很贪玩。

描述

威尼斯是著名的水城。由n个岛和m座桥。岛从1到n编号。
夜夜是一名到威尼斯游玩的游客,她觉得每个岛都很有意思。因此她每次都会随机选择一个与当前岛相邻的岛去参观【同一座岛可以重复参观】。每座桥有一个权值At, 走过这座桥需要花费At个单位的时间, 桥可以双向通过。
夜夜初始在1号岛,她希望最终能到n号岛去。
那么夜夜到达n号岛的期望时间是多少呢?
答案保留一位小数。

格式

输入格式

第一行有两个整数n和m,表示岛屿个数与桥的个数。
之后m行,每行有三个整数u,v和At。其中1<=u,v<=n,表示有一座在岛u和岛v之间的桥,其权值为At。

输出格式

输出答案,答案四舍五入保留到一位小数。

样例1

样例输入1[复制]

3 3
1 2 1
2 3 1
3 1 1

样例输出1[复制]

2.0

样例2

样例输入2[复制]

5 4
1 2 1
2 3 1
3 4 1
4 5 1

样例输出2[复制]

16.0

限制

对于30%的数据
2<=n<=10
对于另外10%的数据
保证数据是一条链
对于另外10%的数据
保证数据是一个环,且1号岛与n号岛相连。
对于100%的数据
2<=n<=200
m<=n(n-1)/2
1<=At<=1000

数据保证每个岛和其余的都是联通的。且图中不存在重边。

每一个测试点时限1秒。




咱自己出的题。。结果自己的程序精度被卡了。。

这题是裸的高斯消元

根据期望的计算方法如下建立方程

for (int i=1;i<n;i++)
{
	double siz=G[i].size();
	mat[i][i]=-siz;
	for (int j=0;j<G[i].size();j++)
		mat[i][G[i][j]]=1.0;
	for (int j=0;j<G[i].size();j++)
		mat[i][n+1]+=(double)G1[i][j];
	mat[i][n+1]/=siz;
}
mat[n][n]=1;

然后你就被卡精度了!!

分析问题所在的话是因为进行了/siz操作

那么怎么办呢?

把所有的方程*siz就可以了

for (int i=1;i<n;i++)
{
	double siz=G[i].size();
	mat[i][i]=-1;
	for (int j=0;j<G[i].size();j++)
		mat[i][G[i][j]]=1.0;
	for (int j=0;j<G[i].size();j++)
		mat[i][n+1]+=(double)G1[i][j];
}
mat[n][n]=1;

然后直接上高斯消元即可

最后要调整一下eps。。。嗯就是这样

#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;

const int Maxn=2000;

double mat[Maxn][Maxn];

vector<int> G[Maxn];
vector<int> G1[2000];
int tot;

void gauss(){
    int now=1;
    for(int i=1;i<=tot;i++){
        int j;
		for(j=now;!mat[j][now]&&j<=tot;j++);
        for(int k=1;k<=tot+1;k++)swap(mat[now][k],mat[j][k]);
        for(int j=1;j<=tot;j++)
            if(j!=now){
                double t=mat[j][now]/mat[now][now];
                for(int k=1;k<=tot+1;k++)
                    mat[j][k]-=t*mat[now][k];
            }
        now++;
    }
    
    for (int i=1;i<=tot;i++) mat[i][tot+1]/=(-mat[i][i]);
}

int n,m;

int main(){
	//freopen("data.in","r",stdin);
	//freopen("data.ans","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		int x,y,t;
		scanf("%d%d%d",&x,&y,&t);
		G[x].push_back(y);
		G[y].push_back(x);
		G1[x].push_back(t);
		G1[y].push_back(t);
	}
	
	tot=n;
	for (int i=1;i<n;i++){
		double siz=G[i].size();
		mat[i][i]=-siz;
		for (int j=0;j<G[i].size();j++){
		//	mat[i][G[i][j]]=1.0/siz;
			mat[i][G[i][j]]=1.0;
			//mat[i][G[i][j]]=1.0/(double)G[G[i][j]].size();
		}
		for (int j=0;j<G[i].size();j++)
		{
			mat[i][n+1]+=(double)G1[i][j];
			//mat[i][G[i][j]]=1.0/(double)G[G[i][j]].size();
		}
		//mat[i][n+1]/=siz;
	}
	mat[n][n]=1;
	/*for (int i=1;i<=n;i++){
		for(int j=1;j<=n+1;j++)
		 cout<<mat[i][j]<<" ";
		cout<<endl;
	}*/
	
	gauss();
	
//	for (int i=1;i<=n;i++){
//		printf("F[%d]=",i);
//		cout<<mat[i][n+1]<<endl;
//	}
	double eps=0.000001;
	printf("%.1f\n",mat[1][n+1]+eps);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值