转载自 http://blog.sina.com.cn/s/blog_5123df350100zp0s.html
照着题解写一次,就当自己懂了。
zoj 和virtual oj 都挂了。好像。。
n个罐子,有一个石头放在第s号罐子里,然后交换这些罐子m次,只能记住k次,每次交换被忘掉的概率相同,那个猜的人(按概率最大的猜)最可能猜哪号罐子里有石头。用dp[i][j][k]表示交换了i次,忘掉了j次,石头在k号罐子里的概率。知道dp[i-1][j][t]开始推,如果它忘掉了这次dp[i][j+1][t]+=dp[i-1][j][t]*p2。如果没忘掉这次,如果这次交换(x,y)有x==t,则dp[i][j][y]+=dp[i-1][j][t]*p1,同理如果y==t则dp[i][j][y]+=dp[i-1][j][t]*p1;否则的话dp[i][j][t]=dp[i-1][j][t]*p1。其中p1是没忘记的概率,p2是忘记的概率p1=k/m , p2=1-p1。 初始dp[0][0][s]=1。最终求argmax{dp[m][m-k][x]}。
#include<stdio.h>
#include<algorithm>
#include<string.h>
struct sp
{
int x,y;
}p[100];
double dp[100][100][100];
int main()
{
// freopen("F:\\123.txt","r",stdin);
int t,m,n,k,s;
scanf("%d",&t);
for(int ik=1;ik<=t;ik++)
{
scanf("%d%d%d%d",&n,&m,&k,&s);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&p[i].x ,&p[i].y );
}
memset(dp,0,sizeof(dp));
double p1=(double)k/m,p2=(double)1-p1;
// printf("%f %f\n",p1,p2);
dp[0][0][s]=1;
for(int i=1;i<=m;i++)
{
for(int j=0;j<=m-k &&j<=i;j++)
{
for(int u=1;u<=n;u++)
{
dp[i][j+1][u]+=dp[i-1][j][u] *p2;
// printf("see %d %d\n",p[i].x ,u);
if(p[i].x ==u )
{
dp[i][j][p[i].y ]+=dp[i-1][j][u]*p1;
// printf("look %d %f\n",p[i].y ,dp[i][j][p[i].y ]);
}else if(p[i].y ==u)
{
dp[i][j][p[i].x ]+=dp[i-1][j][u]*p1;
}
else
{
dp[i][j][u]+=dp[i-1][j][u];
/* 题解一开始漏了说这种转移情况,然后我用codeblock演算过程,发现当 p[i].x ==u 和 p[i].y ==u 都不成立的时候,应该
有 dp[i][j][u]+=dp[i-1][j][u]; 情况,即是看到了,但是交换的两个罐子都不含有硬币。。。即当前状态,需要继承i-1时的状态*/
}
}
}
}
int maxi=1;
for(int i=1;i<=n;i++)
{
// printf("%d %f\n",i,dp[m][m-k][i]);
if(dp[m][m-k][i]>dp[m][m-k][maxi])
{
maxi=i;
}
}
printf("%d\n",maxi);
}
return 0;
}