poj-2057 The Lost House

31 篇文章 0 订阅
30 篇文章 0 订阅

题意:

给出一颗有根树,边权均为1;

一个S在根结点上,要找到在某个叶子结点上的它的房子;

有的结点上有w,可以告诉S当前结点的子树上是否有它的房子;

房子在每个叶子结点的概率相等,选择一种最佳的计划,来让S走的期望值最小;


题解:

为了这道题去补了补期望是个啥;

简单来说就是,数学期望值=∑ f[x] * p[x]

        f[x]是变量的值,p[x]是变量的概率;

那么,在某种特定的行走计划中,对于某个叶子结点上有房子所需要走的路程,应当是一个定值;

而每个叶子结点的概率都是叶子结点的总数的倒数;

因此,求出一种行走计划,将所有的路程加起来,再除以叶子结点总数,就是题中所求的期望值;

问题现在是找到一种这样的计划,这个问题的子问题无后效性是很显然的;

那么对一个结点的两个儿子,在决策上应当先走那个呢?

先设状态fa[x]表示x的子树上有房子的期望值;

fb[x]表示x的子树上没有房子的期望值;

令x为父结点,y1,y2为子结点,le[x]指x结点的子树上的叶子数;

那么先走y1的答案就是:

fa[x]=fa[y1]+1*le[y1]+(fb[y1]+2+1)*le[y2]+fa[y2];

// 当在y1子树上答案是fa[y1]+le[y1]因为除了在子树上以外,还有从父亲x走到y1的一步;

//      而在y2上应该是计算了之前子树的(fb[x]+1)*le[y2]表示多走的路,然后再加上fa[y2];

fb[x]=fb[y1]+2+fb[y2]+2;

先走y2的答案是:

fa[x]=fa[y2]+1*le[y2]+(fb[y2]+2+1)*le[y1]+fa[y1];

fb[x]=fb[y2]+2+fb[y1]+2;

可见,fb是不受先走后走的影响的,而将两个决策做比较,令y1优于y2,化简可得;

(fb[y1]+2)*le[y2]<(fb[y2]+2)*le[y1];

于是就可以用这个规律来判断,并且在多个子结点也可以这么搞,以这个排序维护fa,fb就可以了;

结点上有w的时候需要一点特判,就是S到达时立刻可以知道这里有没有房子,所以这里的fb就是0;

然后输出fa[root]/le[root]就可以了,实际上只需要最后输出转化浮点型即可;


代码:


#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1001
using namespace std;
vector<int>to[N];
int le[N],fa[N],fb[N];
bool is[N];
char str[10];
void init(int n)
{
	for(int i=1;i<=n;i++)	to[i].clear();
	memset(fa,0,sizeof(fa));
	memset(fb,0,sizeof(fb));
	memset(le,0,sizeof(le));
	memset(is,0,sizeof(is));
}
int cmp(int a,int b)
{
	return (fb[a]+2)*le[b]<(fb[b]+2)*le[a];
}
void dfs(int x)
{
	le[x]=0;
	int i,y,cnt=0;
	int son[9];
	for(i=0;i<to[x].size();i++)
	{
		y=to[x][i];
		dfs(y);
		le[x]+=le[y];
		son[++cnt]=y;
	}
	sort(son+1,son+1+cnt,cmp);
	for(i=1;i<=cnt;i++)
	{
		fa[x]+=(fb[x]+1)*le[son[i]]+fa[son[i]];
		fb[x]+=fb[son[i]]+2;
	}
	if(!le[x])
		le[x]=1;
	if(is[x])
		fb[x]=0;
}
int main()
{
	int n,m,i,j,k,x,y,root;
	while(scanf("%d",&n)&&n)
	{
		init(n);
		for(i=1;i<=n;i++)
		{
			scanf("%d%s",&x,str);
			if(x!=-1)
				to[x].push_back(i);
			else
				root=i;
			if(str[0]=='Y')
				is[i]=1;
		}
		dfs(root);
		printf("%.4lf\n",((double)fa[root])/le[root]);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值