hdu 3559 Frost Chain 概率dp 宽搜做法

62 篇文章 0 订阅

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3559

 

题意:

       你现在有5个敌人,每个敌人都有一个血量HP值和所在的位置(x,y),现在你要劈一道闪电,闪电先是随机劈这五个人中的一个人,然后跳n次、每跳一次劈一个人,但是它只能跳到和当前这个人距离小于d的HP还未小于等于0的人身上(也就是还没死距离小于等于d的),如果有多个这样的人,那么也会随机选一个劈中,如果找不到人但是还没跳满n次,也会停下,现在问你这5个人HP小于等于0的概率。

做法:

       概率dp,网上有蛮多记忆化搜索的方法,我就写一个不一样的宽搜做法(其实是维度开太多敲起来麻烦。。。而且我也一下子没看太懂咳咳...)。机房里其他人提供的方法,就是开一个6*100000的空间,用每一位代表一个人的血量,(因为题目里说HP<=5,所以最大的状态也就是55555,其实开60000的空间应该也能过),dp[i][j]代表当前劈到这个人i的时候状态j的概率,我还加了一个当前次数num表示跳跃还剩几次,假设有某一位已经为0,那么就把这个概率加进答案里面,我在这里每个状态只会搜一遍,为什么可以直接这样呢,会不会因为1011可以从11011,2011,1111 ..等状态过来,先把1011做掉了就会失去答案,这是不会的,因为队列先进先出,1011肯定是在11011,2011,1111...等后面被塞进去的(次数少一),所以做到这个状态的时候,其实能到达这个状态的所有情况都已经讨论了。所以我加了一个mp来表示这个状态是不是已经被到达过了,没达到的话就把这个状态加入队列就好了。

       队友直接没记录num,用pair进行存取,比我快了好多..


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn=1000005;
double dp[6][maxn];
map<int,int> mps[6];
int n,d,x[6],y[6];
int hp[5],mp[6][6];
double ans[6];
int kill[6]={100000,10000,1000,100,10,1};
struct node{
    int num,sta,peo;
    node(){}
    node(int num,int sta,int peo):num(num),peo(peo),sta(sta){}
};
int isdie(int sta,int peo){
    return (sta%kill[peo-1]/kill[peo])==0;
}
void bfs(){
    queue<node> q;
    int pre=0;
    for(int i=1;i<=5;i++){
        pre=pre*10;
        pre+=hp[i];
    }
    for(int i=1;i<=5;i++){
        int nex=pre-kill[i];
        if(isdie(nex,i)) ans[i]+=0.2;
        dp[i][nex]=0.2;
        q.push(node(n,nex,i));
    }
    while(!q.empty()){
        node u = q.front();q.pop();
        //printf("u:  res= %d, peo = %d, state = %d dp = %.3f\n",u.num,u.peo,u.sta,dp[u.peo][u.sta]);
        double geshu=0;
        for(int i=1;i<=5;i++){
            if(i==u.peo) continue;
            if(mp[u.peo][i]&&!isdie(u.sta,i)) geshu++;
        }
        if(geshu==0){
            continue;
        }
        for(int i=1;i<=5;i++){
            if(i==u.peo) continue;
            if(mp[u.peo][i]&&!isdie(u.sta,i)) {
                int nex_sta=u.sta-kill[i];

                if(isdie(nex_sta,i)){
                    ans[i]=ans[i]+dp[u.peo][u.sta]*1.0/geshu;
                }
                if(u.num!=1){
                    dp[i][nex_sta]+=dp[u.peo][u.sta]*1.0/geshu;
                    //printf("v: res= %d, peo = %d, state = %d dp = %.3f\n",u.num-1,i,nex_sta,dp[i][nex_sta]);
                    if(!mps[i][nex_sta]) {
                        q.push(node(u.num-1,nex_sta,i));
                        mps[i][nex_sta]=1;
                    }
                }
            }
        }
    }
}
int main(){
    while(~scanf("%d%d",&n,&d)){
        memset(mp,0,sizeof(mp));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=5;i++) scanf("%d%d",&x[i],&y[i]);
        for(int i=1;i<=5;i++) scanf("%d",&hp[i]),ans[i]=0.0,mps[i].clear();
        for(int i=1;i<=5;i++){
            for(int j=i+1;j<=5;j++){
                if(((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))<=d*d)
                    mp[i][j]=mp[j][i]=1;
            }
        }
        bfs();
        for(int i=1;i<=5;i++) printf("%.3f%c",ans[i],i==5?'\n':' ');
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值