已知孙尚香具有技能枭姬,当她替换掉现有的装备牌时会新摸两张牌,装备共有4种,现在已知她的手牌情况和牌堆里的牌的情况,初始状态下孙尚香的装备区没有牌,问孙尚香从牌堆里抽取的牌的数量的期望。假设从牌堆里抽取到任意一张牌是等概率的,孙尚香会立刻装备摸到的装备牌。
数据范围:所有的牌的总数不会超过104
动态规划,定义状态dp[i][j][k],i表示装备区状态,范围从0到15,记录4种装备都有没有,j表示牌堆里还有的现在已经装备了该种装备的牌数,k表示牌堆里的其他牌的牌数。dp[i][j][k]表示在此状态下,我们还能额外抽到的牌的期望。
我们可以根据这3个值,计算出是否还有摸牌机会。
状态转移方程:每次摸牌有概率摸到已经有的装备牌(会增加两次摸牌机会),没有的装备牌(改变装备去状态),其他牌(仅消耗了一次摸牌机会)。
#include <cstdio>
#include <cstring>
double dp[16][105][105];
int visited[16][105][105];
int initChance,cas,initState,initI,initJ;
int initDeck[5];
int calChance(int s,int i,int j) {
int ans=initChance+i+j-initI-initJ;
int k,initSum=0;
for (k=0;k<4;k++) {
if ((s&1<<k)!=0) {
initSum+=initDeck[k];
if ((initState&1<<k)==0) initSum--;
}
}
ans+=(initSum-i)*2;
return ans;
}
double cal(int s,int i,int j) {
if (i<0||j<0) return 0;
if (i==0&&j==0) return 0;
if (visited[s][i][j]==cas) return dp[s][i][j];
if (calChance(s,i,j)>0) {
dp[s][i][j]=1+(double)i/(i+j)*cal(s,i-1,j);
int k,l=j;
for (k=0;k<4;k++) {
if ((s&1<<k)==0) {
l-=initDeck[k];
dp[s][i][j]+=(double)initDeck[k]/(i+j)*cal(s|1<<k,i+initDeck[k]-1,j-initDeck[k]);
}
}
dp[s][i][j]+=(double)l/(i+j)*cal(s,i,j-1);
} else dp[s][i][j]=0;
//printf("%d %d %d: %d %lf\n",s,i,j,calChance(s,i,j),dp[s][i][j]);
visited[s][i][j]=cas;
return dp[s][i][j];
}
int main() {
int t,i,j,x;
scanf("%d",&t);
memset(visited,0,sizeof(visited));
for (cas=1;cas<=t;cas++) {
initChance=0;
initState=0;
for (i=0;i<4;i++) {
scanf("%d",&x);
if (x>0) initState|=1<<i;
if (x>1) initChance+=(x-1)*2;
}
scanf("%d",&x);
initI=initJ=0;
for (i=0;i<5;i++) {
scanf("%d",&initDeck[i]);
if (i<4&&(initState&1<<i)!=0) initI+=initDeck[i];
else initJ+=initDeck[i];
}
printf("Case %d: %.2lf\n",cas,cal(initState,initI,initJ));
}
return 0;
}