题意:
n个枪手站在一起,轮流射击,每次只能射一发,且只能射自己以外的人,命中即死。每个人每次射击都有一个命中率。游戏到剩下最后一个人存活结束。每个人每次射击会用最优决策(决策后,自己存活率最高),如果有多个最优决策,随机。
问n(n<=13)个人存活的概率。
:
状态的表示是[i][S][j]表示当前存活的状态S,i表示当前该谁射击,j表示此时j的胜率。
状态转移存在环,[i][S]这个状态可能多次转移回到[i][S]
设b[i][k]为i命中时k的胜率,a[i][k]为i未命中k的胜率,p[i]为i命中的概率,q[i]为i未命中的概率。
则dp[cur][state][k]=p[cur]*b[cur][k]+q[cur]*a[cur][k]
b[cur][k]比较好算,先求出每个子状态,然后统计最优解的个数。
a[cur][k]=a[cur+1][k]*q[cur+1]+b[cur+1][k]*p[cur+1]
= (a[cur+2][k]*q[cur+2]+b[cur+2][k]*p[cur+2])*q[cur+1]+b[cur+1][k]*p[cur+1]
=a[cur][k]* ( q[cur+1] * …*q[cur])+
b[cur+1][k]*p[cur+1]+b[cur+2][k]*(q[cur+1]*p[cur+2])+…+b[cur][k]*(q[cur+1]* … *q[cur-1])*p[cur]
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s) memset(a,x,(s)*sizeof a[0])
#define mem(a,x) memset(a,x,sizeof a)
#define ysk(x) (1<<(x))
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 13 ;
const int maxS= 8192 ;
const double eps=1e-10;
double p[maxn+10],dp[maxn+3][maxS+3][maxn+3];
int n,ed,nex[maxn+3][maxS+3],num[maxS+3];
int next(int cur,int &state)
{
if(~nex[cur][state]) return nex[cur][state];
int &ans=nex[cur][state];
while(1)
{
if(++cur==n) cur=0;
if(state&ysk(cur)) return ans=cur;
}
}
int cal(int s)
{
if(~num[s]) return num[s];
int cnt=0;
while(s)
{
if(s&1) cnt++;
s>>=1;
}
return num[s]=cnt;
}
int dcmp(double x) { if(x>eps) return 1; return x<-eps?-1:0; }
double b[maxn+3][maxn+3];int c[maxn+3][maxn+3];
void dfs(int cur,int state)
{
if(dp[cur][state][0]>=0) return ;//很重要,必须记忆化,虽然没有环,不等于没有子问题重叠
if(cal(state)==1)
{
for0(k,n) dp[cur][state][k]= ( state&ysk(k) )?1:0;
return;
}
for0(i,n) if(state&ysk(i))
{
for(int tag=next(i,state);tag!=i ;tag=next(tag,state) )
{
int ns=state^(ysk(tag));
dfs(next(i,ns),ns );
}
}
for0(i,n) for0(k,n) b[i][k]=c[i][k]=0;
for0(i,n) if(state&ysk(i))
{
double maxP=0;
for(int tag=next(i,state);tag!=i;tag=next(tag,state))
{
int ns=state^(ysk(tag));
maxP=max(maxP,dp[next(i,ns)][ns][i] );
}
for(int tag=next(i,state);tag!=i;tag=next(tag,state))
{
int ns=state^(ysk(tag));int shooter=next(i,ns);
if(dcmp(dp[shooter][ns][i]-maxP)==0)
{
for0(k,n)/*这句话if(ysk(k)&ns)不能加,因为即使此时dp[shooter][ns][k]为0
但是,可能k不同时,dp[shooter][ns][i]与此时相等,但是dp[shooter][ns][k]不为0。
故无论dp[shooter][ns][k]为何值,均要统计。
*/
{
c[i][k]++;
b[i][k]+=dp[shooter][ns][k];
}
}
}
for0(k,n) if(c[i][k]) b[i][k]/=c[i][k];
}
for0(k,n) dp[cur][state][k]=p[cur]*b[cur][k];
for0(k,n) if(state&ysk(k))
{
int now=cur;double tmp=1,sum=0;
do
{
now=next(now,state);
sum+=b[now][k]*tmp*p[now];
tmp*=(1-p[now]);
}while(now!=cur);
dp[cur][state][k]+= (1-p[cur])*sum/(1-tmp);
}
}
int main()
{
int T;scanf("%d",&T);
mem(nex,-1);
mem(num,-1);
while(T--)
{
scanf("%d",&n);for0(i,n) {scanf("%lf",&p[i]); p[i]/=100;}
ed=ysk(n)-1;
for0(i,n) for0(s,ed+1) for0(k,n) dp[i][s][k]=-1;
dfs(0,ed );
for0(i,n) printf("%.2f%c",100*dp[0][ed][i],i==n-1?'\n':' ');
}
return 0;
}