第一次写C++,手生导致此题耗了两个晚上总计6个小时(好在是自己做出来的)(391ms)
题意:
有n个人,站成一个圈。每个人开枪击中别人的概率是固定的(设数组P[i]表示第i个人的命中率),从第一个人开始轮流开枪,求每个人的生还的概率是多少?(假设每人都以最有利自己的方式选择射击对象,并且当有多个目标都一样好时,随机选择其中一个(即m个好对象时,每个对象被他选中的概率为1/m)。并且,每轮都必须射击,虽然故意放弃射击机会可能增大存活的概率)。
不许自杀。
这个题其实是一个环状DP方程应该很好想
F[i,j,s]表示s状态下,轮到第j个人射击时,第i个人存活概率(i,j为原题的第i个和第j个)s为二进制表示的集合
F[i,j,s]=(sigma(F[i,next(j,k,s),s^pow[k-1]]/sigma{k的个数})*P[j]+(1-P[j])*F[i,next(j,s).s]
首先在确定的s下
令A[j]=F[i,j,s] A[j+1]=F[i,next(j,s),s] A[j+2]=F[i,next(next(j,s),s),s] .......
b[j]=(sigma(F[i,next(j,k,s),s^pow[k-1]]/sigma{k的个数})*P[j]
Q[j]=1-P[j]
b[j+1],Q[j+1]所表示的类似于A[j+1]那么按方程展开有A[j]=b[j]+Q[j]a[j+1]=b[j]+Q[j]b[j+1]+Q[j]Q[j+1]a[j+2]=.....................
最后由于有环,展开之后就可以回到A[j]了,且系数为Q[1]到Q[m]的累乘,然后移项相减相除,
Final A[j]=(b[j]+Q[j]b[j+1]+Q[j]Q[j+1]b[j+2]+.......+Q[j]*Q[j+1]*...*Q[j-1]b[j-1])/(1-Q[1]至Q[m]的累乘)
而b[j]的系数也可以找到规律
算法就出来了
STEP 1
枚举S(从1到pow[n]-1)
{
STEP 2
计算每个人的决策;
STEP 3
枚举 i{i in s}
{STEP 4
计算 i 下的 b[j];
STEP 5
枚举j (j in s)计算F[i,j,s];
}
}
复杂度O((n^3)*(2^n))
注意STEP 5有两重循环
每个人的答案就是F[i,1,pow[n]-1]
一下是代码
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define zero(a) ((fabs(a)<eps))/*注意是Fabs,不是abs*/
#define equal(a,b) (zero(((a)-(b))))
const int ppo[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192};
const double eps=1e-12;
int plan[15][15];
double p[15],b[15];
double f[14][14][8192];
int head;
int que[15];
int next[15][8192];
void prepare()
{
memset(p,0,sizeof(p));
}
void calcplan(int now,int state)/*即时计算S下now的决策,注意可能有多个选择,但没有自己!(不许自杀)*/
{
double maxt=0;
for(int l=1;l<=head;l++)
if (que[l]!=now)
{
int kill=que[l];
int k=next[now][state];
if (k==kill) k=next[k][state];
double com=f[now][k][state^ppo[kill-1]];
if (equal(com,maxt))
{
plan[now][0]++;
plan[now][plan[now][0]]=kill;
}
else
if (com>maxt)
{
plan[now][0]=1;
maxt=com;
plan[now][1]=kill;
}
}
}
void ready()/*计算每个S下下个人是谁,存入next数组*/
{
for (int state=1;state<ppo[13];state++)
{
head=0;
memset(que,0,sizeof(que));
int stmp=state;
for(int i=1;i<=13;i++)
{
if (stmp & 1==1)
{
head++;
que[head]=i;
next[que[head-1]][state]=que[head];
};
stmp=stmp>>1;
}
next[que[head]][state]=que[1];
next[0][state]=0;
}
}
int main()
{
int task=0;
ready();
freopen("poj3028.in","r",stdin);
freopen("poj3028.out","w",stdout);
scanf("%d",&task);
for(;task>0;task--)
{
prepare;
int n;
int i;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%lf",&p[i]);
p[i]/=100;
}
int state;
for(state=1;state<ppo[n];state++)
{
memset(plan,0,sizeof(plan));
memset(que,0,sizeof(que));
head=0;
memset(b,0,sizeof(b));
int stmp=state;
int j=0;
double tot=1;
for(i=1;i<=n;i++)
{
if (stmp & 1==1)
{
head++;
que[head]=i;
tot=tot*(1-p[i]);
};
stmp=stmp>>1;
}
if (head==1)
{
f[que[1]][que[1]][state]=1;
continue;
}
/*特殊处理1个人的情况!*/
tot=1-tot;
for(i=1;i<=head;i++)
calcplan(que[i],state);/*记录决策计划中无自己*/
for(int p1=1;p1<=head;p1++)
{
i=que[p1];
for(int p2=1;p2<=head;p2++)/*计算i下的B[j]*/
{
j=que[p2];
double sum=0;
for(int p3=1;p3<=plan[j][0];p3++)
{
int k=next[j][state];
if (k==plan[j][p3]) k=next[k][state];
sum+=f[i][k][state^ppo[plan[j][p3]-1]];
}
sum/=plan[j][0];
b[p2]=sum*p[j];
}
for(int p2=1;p2<=head;p2++)/*计算F[i,j,s]*/
{
j=que[p2];
double sum=0;
double times=1;
for(int p3=p2;p3<=head;p3++)
{
sum+=b[p3]*times;
times*=(1-p[que[p3]]);
}
for(int p3=1;p3<p2;p3++)
{
sum+=b[p3]*times;
times*=(1-p[que[p3]]);
}
f[i][j][state]=sum/tot;
}
}
}
for (int i=1;i<=n;i++)
{
printf("%.2f ",f[i][1][ppo[n]-1]*100.0);
}
printf("\n");
}
}