题目链接: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;
}