//题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4624
/*
题目来源:浙大十周年校赛第B题
题目类型:DP求期望值
结题报告人:SpringWater(GHQ)
题目大意:一个月光宝盒有两个面,每个面有n个洞,每个洞在晚上到来的时候就可能吸收月光的
能量而发亮,每个洞亮的概率为P,当两边同时都至少有m个洞亮时盒子就可以工作了!
问;当盒子工作时要经历的天数的期望值!
解题思路:首先考录到天数可能是无限,所以不可能算出每种天数再乘上该天数的概率!所以应考虑
将无限的期望值转换为有限的状态方程;
ps:下面这个状态方程虽然是绝对本人的原创,没借鉴任何解题报告,但是在之前我已经看了
其他很道题(如:成都网络赛的Maze即杭电4035)用DP求期望值类型的题,所以再回头做
这题的时候就感觉just a cake!所以高难度算法题,还是的事先多练,才能在比赛中AC,否则
如果你想在比赛中现成的想出这等法来ac它,那可能是一个比泡到凤姐都更遥远的梦!!
令:E[i][j]为当前左右面分别已经亮了i,j个洞,E[0][0]为所求!
E[i][j]=∑p(i+m,j+n)(E[i+m][j+n]+1) (E[i][j]为当前已经左边有i个洞已经亮了,同时右
边j个洞已经亮了的前提下需要天数的期望值,p(i+m,j+n)是指从当前E[i][j]跳转到E[i+m][j+n]
的概率且p(i+m,j+n)=C[N-i][m]*PPow[m]*NPPow[N-i-m]*C[N-j][n])*PPow[n]*NPPow[N-j-n])又
因为E[i][j]==0(当i>=m&&j>=m时)所以当前E[0][0]可由前面的退出来,只用单独考虑一下
(m=0&&n=0)时的情况对于每一个E[i][j]可得到E[i][j]=a*E[i][j]+b;直接解这个一元一次方程就可!
回顾此题感悟:1此类题关键是利用假设前面的(E[i+m][j+n]已经算出来的前提下,将(E[i][j]的状态转移到(E[i+m][j+n]
上即可解决无限次带来的误差与繁琐!
2.做完这题要是在增加:规定当两面分别有确定的x,y个亮时,则原来亮的全部熄灭从新开始,那这个
题的难度又可以增加了!(有兴趣的大牛们不妨思考思考!)其实要是你对此类题比较熟悉的话,一样的可以解决,
因为这题的解法就和成都区域赛的那个题型就很类似了!
/*
题目来源:浙大十周年校赛第B题
题目类型:DP求期望值
结题报告人:SpringWater(GHQ)
题目大意:一个月光宝盒有两个面,每个面有n个洞,每个洞在晚上到来的时候就可能吸收月光的
能量而发亮,每个洞亮的概率为P,当两边同时都至少有m个洞亮时盒子就可以工作了!
问;当盒子工作时要经历的天数的期望值!
解题思路:首先考录到天数可能是无限,所以不可能算出每种天数再乘上该天数的概率!所以应考虑
将无限的期望值转换为有限的状态方程;
ps:下面这个状态方程虽然是绝对本人的原创,没借鉴任何解题报告,但是在之前我已经看了
其他很道题(如:成都网络赛的Maze即杭电4035)用DP求期望值类型的题,所以再回头做
这题的时候就感觉just a cake!所以高难度算法题,还是的事先多练,才能在比赛中AC,否则
如果你想在比赛中现成的想出这等法来ac它,那可能是一个比泡到凤姐都更遥远的梦!!
令:E[i][j]为当前左右面分别已经亮了i,j个洞,E[0][0]为所求!
E[i][j]=∑p(i+m,j+n)(E[i+m][j+n]+1) (E[i][j]为当前已经左边有i个洞已经亮了,同时右
边j个洞已经亮了的前提下需要天数的期望值,p(i+m,j+n)是指从当前E[i][j]跳转到E[i+m][j+n]
的概率且p(i+m,j+n)=C[N-i][m]*PPow[m]*NPPow[N-i-m]*C[N-j][n])*PPow[n]*NPPow[N-j-n])又
因为E[i][j]==0(当i>=m&&j>=m时)所以当前E[0][0]可由前面的退出来,只用单独考虑一下
(m=0&&n=0)时的情况对于每一个E[i][j]可得到E[i][j]=a*E[i][j]+b;直接解这个一元一次方程就可!
回顾此题感悟:1此类题关键是利用假设前面的(E[i+m][j+n]已经算出来的前提下,将(E[i][j]的状态转移到(E[i+m][j+n]
上即可解决无限次带来的误差与繁琐!
2.做完这题要是在增加:规定当两面分别有确定的x,y个亮时,则原来亮的全部熄灭从新开始,那这个
题的难度又可以增加了!(有兴趣的大牛们不妨思考思考!)其实要是你对此类题比较熟悉的话,一样的可以解决,
因为这题的解法就和成都区域赛的那个题型就很类似了!
*/
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<string.h>
using namespace std;
double C[65][65];
double PPow[210],NPPow[210];
int inite() //预处理i个洞里面选j个的选择种数C[i][j]因为只有50*50*25的
{ //复杂度不会超时,果断预处理!以备等会求概率C[N-i][m]*PPow[m+n]*NPPow[N-i-m+N-j-n]*C[N-j][n])使用
int i,j,k,m,top;
for(i=0;i<=60;i++)
{
top=i/2;
for(j=0;j<=60;j++)
{
if(j)
{
if(j<=i)
{
if(j>top)
C[i][j]=C[i][i-j];
else
{
C[i][j]=1;
for(k=1,m=i;k<=j;k++,m--)
{
C[i][j]*=(double)m;
C[i][j]/=(double)k;
}
}
}
else
break;
}
else
C[i][j]=1;
}
}
return 0;
}
int deal(double p,int n)//预处理p^x和(1-p)^x以备等会求概率C[N-i][m]*PPow[m+n]*NPPow[N-i-m+N-j-n]*C[N-j][n])使用
{
int i;
for(i=0;i<=n;i++)
PPow[i]=pow(p,(double)i),NPPow[i]=pow((1-p),(double)i);
return 0;
}
int visit[60][60];
int main()
{
int N,M;
double E[65][65],P,num;
int i,j,m,n;
freopen("input.txt","r",stdin);
inite();
while(scanf("%d%d%lf",&N,&M,&P)&&(N||M))
{
if(fabs(1-P)<1e-20)//因为考虑到m<=n,当p=1是那就只用一个晚上就可达到目标
printf("1.000000\n");
else
{
deal(P,2*N);//预处理
for(i=0;i<=60;i++)
{
for(j=0;j<=60;j++)
E[i][j]=0;
}
memset(visit,0,sizeof(visit));
for(i=N;i>=M;i--)
for(j=N;j>=M;j--)
visit[i][j]=1;//将已经达到宝盒工作的状态排除,以为她们的期望值一定为0,故不用再求
for(i=N;i>=0;i--)
{
for(j=N;j>=0;j--)
{
if(!visit[i][j])//承接上面:将已经达到宝盒工作的状态排除,以为她们的期望值一定为0,故不用再求
{
num=0;
for(m=0;m<=N-i;m++)
{
for(n=0;n<=N-j;n++)//从E[i][j]跳转到E[i+m][j+n]+1),前提(i+m《=n&&j+n《=n)
{
if((m==0)&&(n==0))//当状态转移到自己身上时,不能加上p(i,j)*E[i][j]而应化到左边去,如:求x(x=a*x+b)
num+=NPPow[2*N-i-j];
else
//将除自己以外的所有状态采取期望加权的方式转移
num+=(C[N-i][m]*PPow[m+n]*NPPow[N-i-m+N-j-n]*(E[i+m][j+n]+1)*C[N-j][n]);
}
}
E[i][j]=num/(1-NPPow[2*N-i-j]);
}
}
}
printf("%0.6lf\n",E[0][0]);
}
}
return 0;
}
/*
顺便附上:今天比赛的解题代码:
账号:SpringWater
A题
#include<stdio.h>
int main()
{
double x,sum;
int num;
while(scanf("%lf",&x)!=EOF)
{
num=int(x);
if(num%2)
{
num/=2;
sum=(double)(num+1)*(double)num;
sum+=(num+1)*(x-double((int)x));
}
else
{
num/=2;
sum=(double)(num)*(double)(num-1);
sum+=(double)num;
sum+=(double)num*(x-double((int)x));
}
printf("%0.2lf\n",sum);
}
return 0;
}
D题:
#include<stdio.h>
#include<string.h>
int main(void)
{
char ch,s[1500];
int num,i,mark;
freopen("input.txt","r",stdin);
while(scanf("%c",&ch)!=EOF)
{
if(ch=='\n')
num=0;
else
{
s[0]=ch;
scanf("%s%c",&s[1],&ch);
num=strlen(s);
}
if(num)
{
for(i=0;i<num;i++)
{
if(s[i]=='+'||s[i]=='-')
{
if(!(s[i+1]=='('||((s[i+1]>='0')&&(s[i+1]<='9'))))
break;
}
if(s[i]=='/'||s[i]=='*')
{
if(!i||!((s[i+1]=='(')||((s[i+1]>='0')&&(s[i+1]<='9'))))
break;
}
if(s[i]=='(')
{
if(!(s[i+1]=='+'||s[i+1]=='('||s[i+1]=='-'||((s[i+1]>='0')&&(s[i+1]<='9'))))
break;
}
if(s[i]==')')
{
if(!(s[i+1]=='\0'||s[i+1]==')'||s[i+1]=='*'||s[i+1]=='/'||s[i+1]=='+'||s[i+1]=='-'))
break;
}
if((s[i]>='0')&&(s[i]<='9'))
{
if(s[i+1]=='(')
break;
}
}
if(i<num)
printf("No\n");
else
{
mark=0;
for(i=0;i<num;i++)
{
if(s[i]=='(')
mark++;
if(s[i]==')')
mark--;
if(mark<0)
break;
}
if(mark!=0)
printf("No\n");
else
printf("Yes\n");
}
}
else
printf("\n");
}
return 0;
}
*/

被折叠的 条评论
为什么被折叠?



