HDU 3559 Frost Chain

Frost Chain

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 495    Accepted Submission(s): 214


Problem Description
In the unimaginable popular DotA game, the hero Lich has a wonderful skill: Frost Chain, release a jumping breath of frost that jumps N times against enemy units.



Today iSea play the role of Lich, at first he randomly chooses an enemy hero to release the skill, then the frost jumps for N times. Each time, it make a damage of one HP unit on this hero (including the first time), then bounces to another hero (can’t be himself) if their distance is no more than D and this hero is alive of course, also randomly. Here random means equal probability.

Now we know there are always only five enemy heroes, and also their coordinates and HP value. iSea wonders the death probability of each hero. One hero is dead if its HP is equal to or less than zero.

 

Input
There are several test cases in the input.

Each test case begin with two integers N and D (1 <= N <= 25, 1 <= D <= 10000).
The following line contains ten integers, indicating the coordinates of the five opponents, and -10000 ≤ x, y ≤ 10000.
Then five integers follows, indicating the HP (1 <= HP <= 5) of five opponents.

The input terminates by end of file marker.

 

Output
For each test case, output five floating numbers, indicating the death probability of each hero, as the given order, rounded to three fractional digits, and separated by a single blank.
 

Sample Input
  
  
3 100 0 1 0 2 0 3 0 4 0 5 1 1 1 1 1 3 1 0 1 0 2 0 3 0 4 0 5 1 1 1 1 1
 

Sample Output
  
  
0.800 0.800 0.800 0.800 0.800 0.500 0.800 0.800 0.800 0.500

题意:在DOTA中有个英雄叫LICH,他的大招能弹跳n(n<=25)次(不能弹跳同一个人),作用范围为d,每次命中目标能够扣除对方1点的HP,对方有5个英雄,给出每个英雄的坐标以及HP(HP<=5),每次命中目标后都会自动寻找d范围内的英雄,直到弹跳n次后或者没有其他存活的英雄。求大招结束后每个英雄死亡的概率


题解:

由于英雄总数为5个,HP最多为5,枚举所有状态的状态数也只有6*6*6*6*6=46656种,为了运算方便,我取8*8*8*8*8=262144种状态(只是内存花销大了,由于采用8进制能够使用位运算,所以实际运行速度不会变慢),我们可以采用一个dp数组记录一下每种状态的概率

记dp[i][j][k]为在第j次弹跳,跳到i身上,当前所有英雄状态为k的概率(当然你如果不嫌麻烦也可以开一个7维数组),采用BFS,遍历每一次跳跃能够得到的状态,然后将结果加起来,就能得到答案了


以下为AC代码


#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,status;
bool gap[7][7];//记录两点之间是否能够到达
double dp[7][30][1<<15];
/*
dp[i][j][k]第j次造成伤害,停留在i上,状态为k的概率
*/
double D;
int QK[7*30*(1<<15)],QS[7*30*(1<<15)],QL[7*30*(1<<15)];
/*
模拟队列用,QK用于存储弹跳次数,QS存储状态,QL存储停留在哪个人身上
*/
bool vis[7][30][1<<15];
const int M[5]={28672,3584,448,56,7};
/*
这些数写为2进制为111000000000,000111000000,000000111000,000000000111000,000000000000111,与这些数取与(&)可以判断对应位置的英雄是否存活
*/
const int De[5]={4096,512,64,8,1};
/*
对应英雄的血量减一
*/
double ans[5];


struct coor
{
    double x;
    double y;
}hero[5];


double dis(coor a,coor b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}


int num(int s,int h)//用于计算在状态s时,除了h以外存活的英雄的数量
{
    int cnt=0;
    for(int i=0;i<5;i++)
        if(gap[h][i]&&(s&M[i]))
            cnt++;
    return cnt;
}
void bfs()
{
    int head=0,tail=1;
    QK[0]=0;
    QS[0]=status;
    QL[0]=6;
    dp[6][0][status]=1.0;
    while(head<tail)
    {
        int k=QK[head],s=QS[head],last=QL[head];
        head++;
        if(k==n+1)
            continue;
        if(vis[last][k][s])
            continue;
        vis[last][k][s]=true;
        for(int i=0;i<5;i++)
        {
            int nh=num(s,last);
            if(nh&&(s&M[i])&&gap[i][last])
            {
                double t=dp[last][k][s]/(nh*1.0);
                dp[i][k+1][s-De[i]]+=t;
                if(!((s-De[i])&M[i]))
                    ans[i]+=t;
                if(vis[i][k+1][s-De[i]])
                    continue;
                QK[tail]=k+1;
                QS[tail]=s-De[i];
                QL[tail]=i;
                tail++;
            }
        }
    }
    for(int i=0;i<tail;i++)
    {
        int k=QK[i],s=QS[i],last=QL[i];
        vis[last][k][s]=false;
        dp[last][k][s]=false;
    }
    /*
        注意这里两个case之间不能用memset,不然会TLE,原因是你BFS的过程中有许多状态没有用到
    */
}


int main()
{
    memset(dp,0,sizeof dp);
    memset(vis,0,sizeof vis);
    while(scanf("%d%lf",&n,&D)!=EOF)
    {
        memset(gap,0,sizeof gap);
        for(int i=0;i<5;i++)
        {
            double x,y;
            scanf("%lf%lf",&x,&y);
            hero[i].x=x;
            hero[i].y=y;
        }
        for(int i=0;i<5;i++)
            for(int j=0;j<5;j++)
            {
                if(i==j)
                    continue;
                if(dis(hero[i],hero[j])<=D)
                    gap[i][j]=true;
            }
        status=0;
        for(int i=0;i<5;i++)
        {
            status<<=3;
            int hp;
            scanf("%d",&hp);
            status+=hp;
        }
        for(int i=0;i<=6;i++)//假设一个结点6能够连通所有的结点
            gap[i][6]=gap[6][i]=true;
        for(int i=0;i<5;i++)
            dp[i][0][status]=1.0;
        memset(ans,0,sizeof ans);
        bfs();
        for(int i=0;i<4;i++)
            printf("%.3f ",ans[i]);
        printf("%.3f\n",ans[4]);
    }
    return 0;
}     

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值