HDU 4711 Weather 概率DP

31 篇文章 0 订阅

题意:有个人,他在某个区域待了n天,这个区域有m个地方,有w种天气情况,先给出这个人行程的每天的天气情况,然后给出 从第i个地方到第j个地方的概率,也可以自身到自身,然后给出 某个地方 是某种天气的概率,问你 这个人最优可能的行程路线也就是每天待在哪个地方,

概率DP,求出哪些路线概率最大 再在其中取最小字典序的

假设方程 dp[i][j] 代表 第i天待在j城市,状态转移

dp[i][j] = max(dp[i][j],dp[i - 1][k] * mp[k][j] * pp[j][nnum[i]]);其中mp[i][j]代表由i地方到j地方的概率,pp[j][nnum[i]]代表j这个地方天气为nnum[i]的概率,0<k<m,边界dp[0][0] = 1.00;

然后交了几把发现一直WA,方程没错 觉得是精度问题,因为每次有比较大小的过程,而且乘了2000次,肯定会有误差的,于是改用了long double,但是还是错误,最后还是没做出,后来看题解 发现可以用对数来解决,这高中书白读了....把mp[i][j]  跟pp[i][j]的 概率 取对数 log(mp[i][j]),log(pp[i][j]),这样后面的状态转移方程 乘法就可以写成加法了

dp[i][j] = max(dp[i][j],dp[i - 1][k] + mp[k][j] + pp[j][nnum[i]]);

然后又开始交,发现还是一直WA,因为 mp[i][j]是有可能为0的,意思是 第i个地方不可能到第j个地方,这里要判断一下把 ,若为0 则mp[i][j] = inf,当然 pp[i][j]也有可能为0,在这里判断为是否为0考虑精度问题,我是 判断它是否小于eps,  eps 我精确到了 1e-16次都不行,最后直接改成if(mp[i][j] == 0.00)反而就过了,晕死,坑点好多的题目,

int n,m,w;

int nnum[1000 + 55];

double dp[1000 + 55][100 + 55];

double mp[100 + 55][100 + 55];

double pp[100 + 55][100 + 55];

int father[1000 + 55][100 + 55];

void init() {
	memset(father,0,sizeof(father));
}

bool input() {
	while(cin>>n>>m>>w) {
		for(int i=1;i<=n;i++)
			cin>>nnum[i];
		for(int i=0;i<m;i++)
			for(int j=0;j<m;j++) {
				cin>>mp[i][j];
				if(mp[i][j] == 0.00)
					mp[i][j] = -10000000;
				else mp[i][j] = log(mp[i][j]);
			}
		for(int i=0;i<m;i++)
			for(int j=0;j<w;j++) {
				cin>>pp[i][j];
				if(mp[i][j] == 0.00)
					mp[i][j] = -10000000;
				else pp[i][j] = log(pp[i][j]);
			}
		return false;
	}
	return true;
}

void dfs(int now,int pos) {
	if(now == 1) {
		cout<<pos;
		return ;
	}
	dfs(now - 1,father[now][pos]);
	cout<<" "<<pos;
}

void cal() {
	for(int i=0;i<1000 + 55;i++)
		for(int j=0;j<100 + 55;j++)
			dp[i][j] = -10000000;
	dp[0][0] = (double)0.00;//边界
	for(int i=1;i<n + 1;i++) {
		for(int j=0;j<m;j++) {
			for(int k=0;k<m;k++) {
				//double aa = dp[i - 1][k] + mp[k][j] + pp[j][nnum[i]];
				//double bb = dp[i][j];
				//cout<<aa<<"***"<<endl;
				//cout<<bb<<"***"<<endl;
				if(dp[i - 1][k] + mp[k][j] + pp[j][nnum[i]] > dp[i][j]) {
					dp[i][j] = dp[i - 1][k] + mp[k][j] + pp[j][nnum[i]];
					father[i][j] = k;
				}
			}
		}
	}
	double p = -10000000;
	int s;
	for(int j=0;j<m;j++) {
		//cout<<dp[n][j]<<endl;
		if(dp[n][j] > p) {
			p = dp[n][j];
			s = j;
			//cout<<s<<endl;
			//printf("%.10lf\n",p);
		}
	}
	dfs(n,s);
	puts("");
}

void output() {

}

int main() {
	int t;
	cin>>t;
	while(t--) {
		init();
		if(input())return 0;
		cal();
		output();
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值