1. 程式人生 > >U - The Lost House POJ - 2057 (樹狀dp)

U - The Lost House POJ - 2057 (樹狀dp)

U - The Lost House

 POJ - 2057 

One day a snail climbed up to a big tree and finally came to the end of a branch. What a different feeling to look down from such a high place he had never been to before! However, he was very tired due to the long time of climbing, and fell asleep. An unbelievable thing happened when he woke up ---- he found himself lying in a meadow and his house originally on his back disappeared! Immediately he realized that he fell off the branch when he was sleeping! He was sure that his house must still be on the branch he had been sleeping on. The snail began to climb the tree again, since he could not live without his house. 

When reaching the first fork of the tree, he sadly found that he could not remember the route that he climbed before. In order to find his lovely house, the snail decided to go to the end of every branch. It was dangerous to walk without the protection of the house, so he wished to search the tree in the best way. 

Fortunately, there lived many warm-hearted worms in the tree that could accurately tell the snail whether he had ever passed their places or not before he fell off. 

Now our job is to help the snail. We pay most of our attention to two parts of the tree ---- the forks of the branches and the ends of the branches, which we call them key points because key events always happen there, such as choosing a path, getting the help from a worm and arriving at the house he is searching for. 

Assume all worms live at key points, and all the branches between two neighboring key points have the same distance of 1. The snail is now at the first fork of the tree. 

Our purpose is to find a proper route along which he can find his house as soon as possible, through the analysis of the structure of the tree and the locations of the worms. The only restriction on the route is that he must not go down from a fork until he has reached all the ends grown from this fork. 

The house may be left at the end of any branches in an equal probability. We focus on the mathematical expectation of the distance the snail has to cover before arriving his house. We wish the value to be as small as possible. 

As illustrated in Figure-1, the snail is at the key point 1 and his house is at a certain point among 2, 4 and 5. A worm lives at point 3, who can tell the snail whether his house is at one of point 4 and 5 or not. Therefore, the snail can choose two strategies. He can go to point 2 first. If he cannot find the house there, he should go back to point 1, and then reaches point 4 (or 5) by point 3. If still not, he has to return point 3, then go to point 5 (or 4), where he will undoubtedly find his house. In this choice, the snail covers distances of 1, 4, 6 corresponding to the circumstances under which the house is located at point 2, 4 (or 5), 5 (or 4) respectively. So the expectation value is (1 + 4 + 6) / 3 = 11 / 3. Obviously, this strategy does not make full use of the information from the worm. If the snail goes to point 3 and gets useful information from the worm first, and then chooses to go back to point 1 then towards point 2, or go to point 4 or 5 to take his chance, the distances he covers will be 2, 3, 4 corresponding to the different locations of the house. In such a strategy, the mathematical expectation will be (2 + 3 + 4) / 3 = 3, and it is the very route along which the snail should search the tree. 

Input

The input contains several sets of test data. Each set begins with a line containing one integer N, no more than 1000, which indicates the number of key points in the tree. Then follow N lines describing the N key points. For convenience, we number all the key points from 1 to N. The key point numbered with 1 is always the first fork of the tree. Other numbers may be any key points in the tree except the first fork. The i-th line in these N lines describes the key point with number i. Each line consists of one integer and one uppercase character 'Y' or 'N' separated by a single space, which represents the number of the previous key point and whether there lives a worm ('Y' means lives and 'N' means not). The previous key point means the neighboring key point in the shortest path between this key point and the key point numbered 1. In the above illustration, the previous key point of point 2 or 3 is point 1, while the previous key point of point 4 or 5 is point 3. This integer is -1 for the key point 1, means it has no previous key point. You can assume a fork has at most eight branches. The first set in the sample input describes the above illustration. 

A test case of N = 0 indicates the end of input, and should not be processed. 

Output

Output one line for each set of input data. The line contains one float number with exactly four digits after the decimal point, which is the mathematical expectation value.

Sample Input

5
-1 N
1 N
1 Y
3 N
3 N
10
-1 N
1 Y
1 N
2 N
2 N
2 N
3 N
3 Y
8 N
8 N
6
-1 N
1 N
1 Y
1 N
3 N
3 N
0

Sample Output

3.0000
5.0000
3.5000


題意:一隻蝸牛從樹根尋找丟在樹枝(葉子節點)上的房子,由於毛毛蟲知道毛毛蟲所在的子樹,是否有蝸牛房子的存在,所以選擇不同的訪問路徑找到得到的期望值是不一樣的,問最短的期望值是多少;

思路:這裡我們定義以i為根的子樹,從i出發找到房子的最小期望值為dp[i][1],找不到房子的步數為dp[i][0],那麼對於以i為根的子樹,如果該子樹有a,b兩個孩子子樹,那麼dp[i][1]=min{先訪問a再訪問b的期望值,先訪問b再訪問a的期望值},而

先訪問a再訪問b的期望值ka=房子在孩子子樹a上要走的步數*pa+(房子不在孩子子樹a上要走的步數+房子在孩子子樹b上要走的步數)*pb;

先訪問b再訪問a的期望值kb=房子在孩子子樹b上要走的步數*pb+(房子不在孩子子樹b上要走的步數+房子在孩子子樹a上要走的步數)*pa;

那麼dp[i][1]=min(ka,kb)=min(房子不在孩子子樹a上要走的步數*pb,房子不在孩子子樹b上要走的步數*pa),其中pa為房子在孩子子樹a上的概率,即孩子子樹a上的葉子節點數/總的葉子節點數,下面程式碼用sum[a]表示以孩子子樹a上的葉子節點數,pb同理。那麼現在問題就是比較t1=房子不在孩子子樹a上要走的步數*pb和t2=房子不在孩子子樹b上要走的步數*pa,那麼根據排序,排序的原則是(t1,t2)小者優先,那麼按照這種順序排好的子樹就是小的訪問期望值。

#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int nmax=44;
const double esp = 1e-9;
const double PI=3.1415926;
const int N=1010;
int vis[N],dp[N][2],head[N],sum[N];
struct node
{
    int v,next;
} edge[N];
int cnt;
void add(int u,int v)
{
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
bool cmp(int i,int j)
{
    return (dp[i][0]+2)*sum[j]<(dp[j][0]+2)*sum[i];
}
void dfs(int u)
{
    if(head[u]==-1)
    {
        sum[u]++;
    }
    else
    {
        int tem[11],ans=0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            tem[ans++]=edge[i].v;
            dfs(edge[i].v);
            sum[u]+=sum[edge[i].v];
            if(!vis[u])
                dp[u][0]+=dp[edge[i].v][0]+2;
        }
        sort(tem,tem+ans,cmp);
        int now=0;
        for(int i=0; i<ans; i++)
        {
            dp[u][1]+=dp[tem[i]][1]+(now+1)*sum[tem[i]];  
/*從根出發,房子在子樹tem[i]上要走的步數=以子樹tem[i]為根訪問到房子的期望值+
以u為根,(在子樹tem[i]之前沒有找到房子走的步數now+從根到子樹tem[i]的步數1)*
子樹tem[i]上的葉子結點數,(now+1)*sum[tem[i]]也可以理解訪問子樹tem[i]每一個葉子結點
(有sum[tem[i]]個)之前,要從父結點訪問到孩子結點tem[i]要走的步數,這裡需要好好理解
*/
            now+=dp[tem[i]][0]+2; 
/*now為房子不在子樹tem[i]上,對於根u來說,就要多出房子不在子樹tem[i]上要走的步數+兩步(從根到孩子子樹又回來到更上)
*/
        }
    }
}
int main()
{
    int n,x;
    char ch;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
            break;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        cnt=0;
        scanf("%d %c",&x,&ch);
        for(int i=2; i<=n; i++)
        {
            scanf("%d %c",&x,&ch);
            add(x,i);
            if(ch=='Y')
                vis[i]=1;
        }
        dfs(1);
        printf("%.4lf\n",1.0*dp[1][1]/sum[1]);
    }
    return 0;
}