Bzoj4899--记忆的轮廓

Description

通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,
在[1,n]中,一共有n个节点。我们把编号在[1,n]的叫做正确节点,[n+1,m]的叫做错误节点。一个叶子,如果是正
确节点则为正确叶子,否则称为错误叶子。莎缇拉要帮助昴到达贤者之塔,因此现在面临着存档位置设定的问题。
为了让昴成长为英雄,因此一共只有p次存档的机会,其中1和n必须存档。被莎缇拉设置为要存档的节点称为存档
位置。当然不能让昴陷入死循环,所以存档只能在正确节点上进行,而且同一个节点不能存多次档。因为通往贤者
之塔的路上有影响的瘴气,因此莎缇拉假设昴每次位于树上一个节点时,都会等概率选择一个儿子走下去。每当走
到一个错误叶子时,再走一步就会读档。具体的,每次昴到达一个新的存档位置,存档点便会更新为这个位置(假
如现在的存档点是i,现在走到了一个存档位置j>i,那么存档点便会更新为j)。读档的意思就是回到当前存档点
。初始昴位于1,当昴走到正确节点n时,便结束了路程。莎缇拉想知道,最优情况下,昴结束路程的期望步数是多
少?

 

Input

第一行一个正整数T表示数据组数。
接下来每组数据,首先读入三个正整数n,m,p。
接下来m-n行,描述树上所有的非正确边(正确边即连接两个正确节点的边)
用两个正整数j,k表示j与k之间有一条连边,j和k可以均为错误节点,也可以一个为正确节点另一个为错误节点。
数据保证j是k的父亲。
50<=p<=n<=700,m<=1500,T<=5。
数据保证每个正确节点均有至少2个儿子,至多3个儿子。

 

Output

T行每行一个实数表示每组数据的答案。请保留四位小数。
--------------------------------------此后一千里-------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
题解 :
首先我们设v[i][j]表示前一个存档点为 i ,走到 j 的期望步数。这个可以递推求得。
然后最优解就可以表示为v[1][a1]+v[a1][a2]+...+v[ap][n]
所以最优解可以dp得到。直接dp是n^3的,可能跑不过,观察发现由于题目性质,v[i][j]至少是v[i][j-1]的两倍,
而p大于等于50,所以v[i][i+16]及之后的值都过大,所以我们只用管前16个就可以了。
代码 :
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define LL long long
using namespace std;

#define int int
inline int Max(int a,int b) {return a>b?a:b;}
inline int Min(int a,int b) {return a<b?a:b;}
inline int Sqr(int a) {return a*a;}
inline int Abs(int a) {return a>0?a:-a;}
#undef int

#define MAXN 705

struct Edge{
    int to,next;
}e[MAXN*5];int head[1505],cnt;
inline void Insert(int a,int b) {
    e[++cnt].next=head[a];head[a]=cnt;e[cnt].to=b;
}

double v[MAXN][MAXN],r[1505],E[1505],dp[MAXN][MAXN];
int n,m,p,T;

void Dp(int v) {
    E[v]=0;
    for(int i=head[v];i;i=e[i].next) {
        Dp(e[i].to);E[v]+=E[e[i].to];
    }
    if(r[v]) E[v]/=r[v];
    E[v]++;
}

int main() {
    scanf("%d",&T);
    while(T--) {
        memset(head,0,sizeof(head));cnt=0;
        memset(r,0,sizeof(r));
        scanf("%d%d%d",&n,&m,&p);p--;
        for(int a,b,i=n+1;i<=m;i++) {
            scanf("%d%d",&a,&b);
            Insert(a,b);r[a]++;
        }
        for(int i=1;i<n;i++) r[i]++;
        for(int i=1;i<=n;i++) {
            E[i]=0;
            for(int j=head[i];j;j=e[j].next) {
                Dp(e[j].to);E[i]+=E[e[j].to];
            }
        }
        for(int i=1;i<=n;i++) 
            for(int j=i+1;j<=n;j++) {
                if(i-j>16) break;
                v[i][j]=E[j-1]+r[j-1]+r[j-1]*v[i][j-1];
            }
        for(int i=1;i<=n;i++) for(int j=0;j<=p;j++) dp[i][j]=1e20;
        dp[n][0]=0;
        for(int i=n;i;i--) 
            for(int j=1;j<=p;j++) 
                for(int k=i+1;k<=n;k++) {
                    if(k-i>16) break;
                    dp[i][j]=min(dp[i][j],v[i][k]+dp[k][j-1]);
                }
        printf("%.4lf\n",dp[1][p]);
    }
    return 0;
}
View Code

 

posted @ 2017-05-26 11:50  ihopenot  阅读(266)  评论(0编辑  收藏  举报