CTSC2011 幸福路径

传送门 https://www.luogu.org/problemnew/show/P4308

给定一张有向图,每个点有个权值,蚂蚁从某个点开始,初始体力为1,每经过一条边,体力会变为原来的p(0 < p < 1)倍,每爬到一个点,获得的幸福度为该点的权值乘上体力。求蚂蚁幸福度的最大值,保留一位小数。

这道题最开始拿到的时候想到用期望dp,然后自己推出了一个式子,感觉没什么问题,交上去之后发现只得了40分,然鹅不造哪里错了。最后看到大佬们都用FLOYD做的。

考虑到体力是按指数级衰减的,所以我们只要做下面的一个dp就可以解决问题了。

f[t][i][j]表示走2t步,从i走到j获得的最大幸福度。

f[t][i][j] = max(f[t - 1][i][k] + f[t - 1][k][j] * p2t - 1)

t足够大时,f得到的就近似为最大幸福度,或者说当p < eps 时,得到的就近似为最大幸福度。

 

#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << "  ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 110
using namespace std;
int n,m,s;
double p,ans,v[N],f[N][N],dp[N][N];
void read1(){
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)
        scanf("%lf",&v[i]);
    scanf("%d",&s);
    scanf("%lf",&p);
    return ;
}
void read2(){
    while(m--) {
        int u,x;
        scanf("%d%d",&u,&x);
        f[u][x] = v[x] * p;
    }
}
void init(){
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i != j)
                f[i][j] = -1e100;
    return ;
}
void solve(){
    for(; p > 1e-10; p *= p) {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                dp[i][j] = -1e100;
        for(int k = 1; k <= n; k++)
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    dp[i][j] = max(dp[i][j],f[i][k]+f[k][j]*p);
        memcpy(f,dp,sizeof(dp));
    }
    return ;
}
void print(){
    for(int i = 1; i <= n; i++)
        ans = max(ans,f[s][i]);
    printf("%.1f\n",ans + v[s]);
    return ;
}
int main() {
    read1();
    init();
    read2();
    solve();
    print();
    return 0;
}

 

posted @ 2019-04-07 20:11  优秀的渣渣禹  阅读(203)  评论(0编辑  收藏  举报