HDU 4579

题意:

          有n个节点,要从第一个节点移动到第n个节点,每次移动可能前进或者后退m步,概率通过给出的n×m的矩阵计算,问从第一个点道第n个点所走步数的数学期望。


思路:

          n个未知数,设F[i]表示从第i点道第n点的期望的步数,则F[n] = 0, 可以列出n-1个F[i]和其他的F[]关系的方程。

         n有50000,直接消元不仅会TLE,还会MLE,这里考虑到第i个方程至多只要前m个方程就能将该方程的前i-1消为0,所以可以滚动,每次只保存m+1个方程。然后模拟高斯消元即可。另外由于要求得最终是F[1],而在滚动过程中F[1]对应矩阵中的行一般会被覆盖掉,这里可以另外弄一行存F[1]对应的行,每次消元时记得把这行也更新一下即可。


代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <string>
#include <map>
#include <cmath>
using namespace std;

#define N 101000
#define M 30
const double eps = 1e-8;
double c[N][M], mat[M][N];
double fir[N];
int n, m;
int dcmp(double x)
{
	if(abs(x) < eps) return 0;
	return x > 0 ? 1 : -1;
}
double getP(int i, int j)
{
	if(abs(i-j) > m) return 0;
	double sum = 0;
	for(int k = 1; k <= m; k++)
	{
		sum += c[i][k];
	}
	if(i-j >= 1)
	{
		return 0.3*c[i][i-j]/(1+sum);
	}
	else return 0.7*c[i][j-i]/(1+sum);

}
void getRow(double* a, int row)
{
	double sum = 0;
	for(int i = max(1, row-m); i <= min(n, row+m); i++)
	{
		if(i != row)
		{
			a[i] = -getP(row, i);
			sum += a[i];
 		}
	}
	a[n+1] = 1;
	a[row] = -sum;
}
int main()
{
    //freopen("/home/zfh/桌面/out", "r", stdin);
	while(scanf("%d%d", &n, &m) && n)
	{
		memset(mat, 0, sizeof(mat));
		memset(fir, 0, sizeof(fir));
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= m; j++)
			{
				scanf("%lf", &c[i][j]);
			}
		}
		int row = 0, col = 1;
		getRow(mat[0], 1);
		getRow(fir, 1);
		for(int i = 2; i < n; i++, col++)
		{
			int cur = (i-1)%(m+1);
			getRow(mat[cur], i);
			int p = row;
			int st = max(i-m, 1);
			for(int j = 1; j <= min(i-1, m); j++)
			{
				double temp = -mat[cur][st]/mat[p][st];
				for(int k = st++; k <= min(st+2*m+1, n); k++)
				{
					mat[cur][k] += temp*mat[p][k];
				}
				mat[cur][n+1] += temp*mat[p][n+1];
				p = (p+1)%(m+1);
			}
			double temp = -fir[i]/mat[cur][i];
			for(int j = i; j <= min(i+m, n); j++)
			{
				fir[j] += temp*mat[cur][j];
			}
			fir[n+1] += temp*mat[cur][n+1];
			if(i > m)
				row = (row+1)%(m+1);
		}
		printf("%.2lf\n", fir[n+1]/fir[1]);
	}
    return 0;
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值