HDU 4418 Time travel

很普通的概率DP的一道题

题意大概是:K要通过时间传送机从X到达Y抓捕犯人,一共n个时间点,每次传送有可能经过m(<m)个时间点才会停下来,并且移动方向有一定规律如0123210……

那么很明显,我们可以将每个点按照方向进行拆分,为什么会想到这一点呢?因为对于每一个点,从左边到达该点的期望和从右边到达该点的期望一般是不相同的(除了端点,因为它没办法从另一边到达该点),因此就把所有点整合为2*n-2个,设dp[x]为到达x时间点的期望个数,那么dp[x]=Σ(dp[x-i]+i)*p[i](1<=i<=m)。再高斯消元就可以得到了。其中还有一点问题,就是m的值有可能大于n的值的,即同一个方程可能出现两次同一未知量,在下面的代码中会有所体现

#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=205;
const double eps=1e-8;
double p[SIZEN];
double dp[SIZEN][SIZEN];
int sum,cnt,Hash[SIZEN];
int m,n,x,y,d;
double tt;
void init(){
    memset(Hash,-1,sizeof(Hash));
    memset(dp,0,sizeof(dp));
    tt=0;
    sum=2*n-2;
}
void guass(){
    int i,j,r,c;
    for(r=c=0;r<sum&&c<sum;r++,c++){
        for(i=r;i<sum;i++)
            if(dp[i][c]!=0) break;
        if(i==sum) break;
        if(i!=r) for(j=c;j<=sum;j++) swap(dp[i][j],dp[r][j]);
        for(i=r+1;i<sum;i++){
            if(dp[i][c]!=0){
                double tmp=dp[i][c]/dp[r][c];
                for(j=c;j<=sum;j++) dp[i][j]-=dp[r][j]*tmp;
            }
        }
    }
    for(i=sum-1;i>=0;i--){
        dp[i][sum]/=dp[i][i];
        dp[i][i]=1;
        for(j=i-1;j>=0;j--)
            dp[j][sum]-=dp[j][i]*dp[i][sum];
    }
}
bool zero(double a){
    return fabs(a)<eps;
}
int bfs(int s){
    cnt=0;
    int flag=0;
    int vis[SIZEN];
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(s);
    Hash[s]=cnt++;
    while(!q.empty()){
        int u=q.front();q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        if(u==y||u==sum-y){
            dp[Hash[u]][Hash[u]]=1;
            dp[Hash[u]][sum]=0;
            flag=1;
            continue;
        }
        dp[Hash[u]][Hash[u]]=1;
        for(int i=1;i<=m;i++){
            if(zero(p[i])) continue;
            int v=(u+i)%sum;
            if(Hash[v]==-1) Hash[v]=cnt++;
            dp[Hash[u]][Hash[v]]-=p[i];//如果为dp[Hash[u]][Hash[v]]=-p[i];就会出错
            dp[Hash[u]][sum]+=p[i]*i;
            q.push(v);
        }
    }
    return flag;
}
void debug(){
    int i,j;
    for(i=0;i<sum;i++){
        for(j=0;j<=sum;j++) printf("%6.2lf",dp[i][j]);
        printf("\n");
    }
}
int main()
{
    //freopen("data.in","r",stdin);
    int i,j,_;
    scanf("%d",&_);
    while(_--){
        scanf("%d%d%d%d%d",&n,&m,&y,&x,&d);
        init();
        for(i=1;i<=m;i++){
            scanf("%lf",&p[i]);
            p[i]/=100;
        }
        if(x==y) {printf("0.00\n");continue;}
        if(d) x=(sum-x)%sum;
        if(!bfs(x)){
            printf("Impossible !\n");
            continue;
        }
        for(i=0;i<sum;i++) swap(dp[i][cnt],dp[i][sum]);
        sum=cnt;
        guass();
        printf("%.2f\n",dp[Hash[x]][sum]);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值