[HDOJ 4865] Peter's Hobby [概率DP]

69 篇文章 0 订阅
3 篇文章 0 订阅

已知第一天的天气的概率,根据今天的天气可以知道下一天的天气的概率,根据当天的天气可以知道当天的叶子湿度的概率。

现给你n天的叶子湿度,求一个最可能的天气序列。

定义状态dp[i][j]为前i天,叶子序列为题目给出条件下的概率

则dp[i][j]=max{dp[i][k]*a[k][j]*b[j][l]},其中a[k][j]表示前一天天气为k的条件下,今天天气为j的概率,b[j][l]表示当天天气为j的条件下,当天叶子湿度为l的概率,l为题目给出。

实际由于数字可能太小趋近于0,所以使用log来防止精度丢失

#include <cstdio>
#include <cstring>
#include <cmath>

double aa[3][4]={
	{0.6,0.2,0.15,0.05},
	{0.25,0.3,0.2,0.25},
	{0.05,0.1,0.35,0.5} };
double bb[3][3]={
	{0.5,0.375,0.125},
	{0.25,0.125,0.625},
	{0.25,0.375,0.375} };
double sstart[3]={0.63,0.17,0.2};
double start[3];
double a[3][4];
double b[3][3];
char weather[3][10]={"Sunny","Cloudy","Rainy"};
int n;
double dp[50][3];
int leaf[50];
int ans[50];
int from[50][3];

int leaftoint(char s[]) {
	if (s[0]=='S') return 3;
	if (s[1]=='a') return 2;
	if (s[3]=='\0') return 0;
	return 1;
}

int main() {
	int t,tt,i,j,k;
	char s[10];
	scanf("%d",&t);
	for (i=0;i<3;i++)
		for (j=0;j<4;j++)
			a[i][j]=log(aa[i][j]);
	for (i=0;i<3;i++)
		for (j=0;j<3;j++)
			b[i][j]=log(bb[i][j]);
	for (i=0;i<3;i++)
		start[i]=log(sstart[i]);
	for (tt=1;tt<=t;tt++) {
		scanf("%d",&n);
		for (i=0;i<n;i++) {
			scanf("%s",s);
			leaf[i]=leaftoint(s);
		}
		memset(from,-1,sizeof(from));
		dp[0][0]=start[0]+a[0][leaf[0]];
		dp[0][1]=start[1]+a[1][leaf[0]];
		dp[0][2]=start[2]+a[2][leaf[0]];
		for (i=1;i<n;i++) {
			for (j=0;j<3;j++) {
				for (k=0;k<3;k++) {
					double tmp=dp[i-1][k]+b[k][j]+a[j][leaf[i]];
					//printf("%d %d %d %lf %lf\n",i,j,k,tmp,dp[i][j]);
					//printf("== %lf*%lf*%lf\n",dp[i-1][k],bb[k][j],aa[j][leaf[i]]);
					if (from[i][j]==-1||dp[i][j]<tmp) {
						from[i][j]=k;
						dp[i][j]=tmp;
					}
				}
			}
		}
		if (dp[n-1][0]>dp[n-1][1]&&dp[n-1][0]>dp[n-1][2]) ans[n-1]=0;
		else if (dp[n-1][1]>dp[n-1][2]) ans[n-1]=1;
		else ans[n-1]=2;
		for (i=n-1;i>0;i--) {
			ans[i-1]=from[i][ans[i]];
		}
		printf("Case #%d:\n",tt);
		for (i=0;i<n;i++) {
			printf("%s\n",weather[ans[i]]);
		}
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值