「some」概率和期望习题总结
Problem 1
设\(dp[u]\)表示从\(u\)到\(n\)所经过的路径总长度的期望,从\(u\)出发有\(k\)条边,指向的结点为\(v_i\),边长分别为\(w_i\),则有
显然\(dp[n]=0\)
建反图,跑\(topo\)就可以了
Code:
#include<cstdio>
#include<queue>
#define MAX 100001
#define re register
namespace OMA
{
int n,m;
double dp[MAX];
double du[MAX],out[MAX];
int cnt=1,head[MAX];
struct node
{
int next;
int to;
int w;
}edge[MAX<<1];
std::queue<int>q;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v,int w)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
edge[cnt].w = w;
head[u] = cnt;
}
void in()
{
dp[n = read()] = 0,m = read();
for(re int i=1; i<=m; i++)
{
int u=read(),v=read(),w=read();
add(v,u,w);
du[u]++,out[u]++;
}
}
void ans()
{
q.push(n);
while(!q.empty())
{
int k = q.front();
q.pop();
for(re int i=head[k]; i; i=edge[i].next)
{
int w = edge[i].w;
int to = edge[i].to;
dp[to] += (dp[k]+w)/out[to];
if(!--du[to])
{ q.push(to); }
}
}
printf("%.2lf\n",dp[1]);
}
signed main()
{
in();
ans();
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 2
Problem 3
3次的不好想,则从1次入手,设\(a[i]\)表示前i位中第\(i\)位为1时的期望分数,则
\(a[i]=(a[i-1]+1)\times p\),
由\((a+1)^2=a^2+2\times a+1\)可递推到2次,
\(b[i]=(b[i-1]+2\times a[i-1]+1)\times p\),
同理,则3次
\(dp[i]=(dp[i-1]+3\times a[i-1]+3\times b[i-1]+1)\times p+dp[i-1]\times (1-p)\)
化简后则有
\(dp[i] = dp[i-1]+(3\times a[i-1]+3\times b[i-1]+1)\times p\)
如果还是不理解推导过程的话,可以去看这篇blog
注意他3次的式子的左括号的位置是错的,重点看推导过程就好
Code:
#include<cstdio>
#include<cmath>
#define MAX 100001
#define re register
namespace OMA
{
int n;
//double a,b,ans;
double dp[MAX];
double a[MAX],b[MAX];
inline double dread()
{
int s=0,w=1,k=0,a=0,b=0;
char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while((ch>='0'&ch<='9')||ch=='.')
{
if(ch=='.'){ a=1; }
else if(!a)
{ s=s*10+ch-'0'; }
else
{ k=k*10+ch-'0'; b++; }
ch = getchar();
}
return (pow(0.1,b)*k+s)*w;
}
signed main()
{
scanf("%d",&n);
for(re int i=1; i<=n; i++)
{
double p=dread();
a[i] = (a[i-1]+1)*p;
b[i] = (b[i-1]+2*a[i-1]+1)*p;
dp[i] = dp[i-1]+(3*a[i-1]+3*b[i-1]+1)*p;
/*ans = ans+(3*a+3*b+1)*p;
b = (b+a*2+1)*p;
a = (a+1)*p;*/
// 两种打法都可以,第二种空间小,但时间都是差不多的
}
printf("%.1lf",dp[n]);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 4
状态转移方程还是很好推的
设\(dp[i][j]\)表示选了\(i\)张红牌,\(j\)张黑牌时的最优策略,
则有
\(dp[i][j]=\max(\frac{i}{i+j}\times(dp[i-1][j]+1)+\frac{j}{i+j}\times(dp[i][j-1]-1),0)\),其中\(dp[i][0]=i\)
至于四舍五入,输出时处理一下进位就好
同时注意,强制转换类型时一定要加括号!!!
因为这个调了一个半小时QAQ
某oj上卡空间,请使用滚动数组,但我不会QAQ
Code:
#include<cstdio>
#define re register
#define MAX 5001
namespace OMA
{
int r,b;
double ans,dp[MAX][MAX];
inline double max(double a,double b)
{ return a>b?a:b; }
signed main()
{
scanf("%d%d",&r,&b);
for(re int i=1; i<=r; i++)
{
dp[i][0] = i;
for(re int j=1; j<=b; j++)
{
ans=0;
ans += (dp[i-1][j]+1)*((double)i/(i+j));
ans += (dp[i][j-1]-1)*((double)j/(i+j));
dp[i][j] = max(ans,0);
}
}
ans = dp[r][b]-0.0000005;
printf("%.6lf",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 5
Problem 6
有点类似第三题
设\(l[i]\)表示以\(i\)结尾时的长度期望,\(dp[i]\)表示第\(i\)位的分数期望,
则会有以下三种情况:
\(ch[i]=o\) 时, \(l[i]=l[i-1]+1\) , 同第三题,通过\(\left(l+1\right)^2=l^2+2 \times l+1\)推出 \(dp[i]=dp[i-1]+2\times l[i-1]+1\)
\(ch[i]=x\) 时, \(l[i]=0\) , \(dp[i]=dp[i-1]\)
\(ch[i]=?\) 时, \(l[i]=\left(l[i-1]+1\right)/2\) , \(dp[i]=\left(2\times dp[i-1]+2\times l[i-1]+1\right)/2\)
Code:
#include<cstdio>
#define re register
#define MAX 300001
namespace OMA
{
int n;
char ch[MAX];
double l[MAX],dp[MAX];
signed main()
{
scanf("%d",&n),scanf("%s",ch+1);
for(re int i=1; i<=n; i++)
{
if(ch[i]=='o')
{
l[i] = l[i-1]+1;
dp[i] = dp[i-1]+2*l[i-1]+1;
}
else if(ch[i]=='x')
{ dp[i] = dp[i-1]; }
else if(ch[i]=='?')
{
l[i] = (l[i-1]+1)/2;
dp[i] = dp[i-1]+(2*l[i-1]+1)/2;
}
}
printf("%.4lf",dp[n]);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 7
我tm就是狗屎
好吧,我还是太菜了
对答案的情况分类讨论,则有以下三种:
\(a_{i-1}=a_i\),则做对的期望为\(\frac{1}{a_i}=\frac{1}{a_{i-1}}\)
\(a_{i-1}>a_i\),则答案在\(1\;~\;a_i\)中的概率为\(\frac{a_i}{a_{i-1}}\),则做对的期望为\(\frac{a_i}{a_{i-1}}\times \frac{1}{a_i}=\frac{1}{a_{i-1}}\)
\(a_{i-1}<a_i\),则答案在\(1\;~\;a_{i-1}\)中的概率为\(\frac{a_{i-1}}{a_i}\),则做对的期望为\(\frac{a_{i-1}}{a_i}\times \frac{1}{a_{i-1}}=\frac{1}{a_i}\)
综上
Code:
#include<cstdio>
#define re register
#define MAX 10000001
namespace OMA
{
int n,A,B,C;
int a[MAX];
double ans;
const int p=100000001;
inline int max(int a,int b)
{ return a>b?a:b; }
signed main()
{
scanf("%d%d%d%d%d",&n,&A,&B,&C,a+1);
for(re int i=2; i<=n; i++)
{ a[i] = ((long long)a[i-1]*A+B)%p; }
for(re int i=1; i<=n; i++)
{ a[i] = a[i]%C+1; }
//for(re int i=1; i<=n; i++)
//{ printf("%d ",a[i]); }
a[0] = a[n];
for(re int i=1; i<=n; i++)
{ ans += (double)1/max(a[i],a[i-1]); }
printf("%.3lf",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 8
列队春游链接可能会挂
zjx写了一篇,所以不打算写了,其实是太懒了
所以贴个式子和代码吧
其中\(top\)为预处理出来的最高的高度,\(cnt[i]\)表示当前高度的有多少人,\(sum\)为比当前这个高度要矮的总人数
其实可以不用预处理,范围小,倒是无所谓
Code:
#include<cstdio>
#define MAX 1001
#define re register
namespace OMA
{
int n;
int top,sum;
double ans;
int high,cnt[MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int max(int a,int b)
{ return a>b?a:b ;}
signed main()
{
n=read();
for(re int i=1; i<=n; i++)
{ top = max(top,high = read()); cnt[high]++; }
for(re int i=1; i<=top; i++)
{
if(cnt[i])
{
ans += (double)cnt[i]*(n+1)/(n-sum+1);
sum += cnt[i];
}
}
printf("%.2lf\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 9
矩形粉刷链接同样可能会炸
hjz写了一篇,所以,没错,我又鸽了
所以化简搞麻烦了一下式子
\(a=h^2\times \left[{(w-i)^2+(i-1)^2}\right]+w^2\times \left[(h-j)^2+(j-1)^2\right]\)
\(b=\left[(h-j)^2+(j-1)^2\right]\times \left[(i-1)^2+(w-i)^2\right]\)
记得容斥
Code:
#include<cstdio>
#define re register
namespace OMA
{
int k,w,h;
double ans,all;
inline double quickpow(double a,int b)
{
double ans=1;
while(b)
{
if(b&1)
{ ans = ans*a; }
a = a*a;
b >>= 1;
}
return ans;
}
inline double mul(double a)
{ return a*a; }
inline double solve(int i,int j)
{
double now;
double a,b/*,c,d*/;
/*a = (double)(mul((w-i)*h)+mul((h-j)*w));
b = (double)(mul((i-1)*h)+mul((j-1)*w));
c = (double)(mul((h-j)*(i-1))+mul((w-i)*(j-1)));
d = (double)(mul((i-1)*(j-1))+mul((w-i)*(h-j)));*/
a = (double)(mul(h)*(mul(w-i)+mul(i-1))+mul(w)*(mul(h-j)+mul(j-1)));
b = (double)(((mul(h-j)+mul(j-1)))*(mul(i-1)+mul(w-i)));
now = 1-(double)(quickpow((a-b)/all,k));
return now;
}
signed main()
{
scanf("%d%d%d",&k,&w,&h);
all = mul(w*h);
for(re int i=1; i<=w; i++)
{
for(re int j=1; j<=h; j++)
{ ans += solve(i,j); }
}
printf("%.0lf\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 10
直接求\(n\)人中每个人的获胜概率不好求,所以从前往后推
设\(dp[i][j]\)表示还剩下\(i\)个人时,第\(j\)个人获胜的概率
,现在选择的牌使第\(p\)个人淘汰,所以
如果\(p>j\),则在新的环中,第\(j\)个人的位置为\(i-p+j\)
如果\(p<j\),则在新的环中,第\(j\)个人的位置为\(j-p\)
则有
最后记得输出 \(\color{Red}{!\;!\;!}\)
Code:
#include<cstdio>
#define MAX 51
#define re register
namespace OMA
{
int n,m;
int num[MAX];
double dp[MAX][MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
signed main()
{
n=read(),m=read();
for(re int i=1; i<=m; i++)
{ num[i] = read(); }
dp[1][1] = 1;
for(re int i=2; i<=n; i++)
{
for(re int j=1; j<=i; j++)
{
for(re int k=1; k<=m; k++)
{
int pos = num[k]%i;
if(!pos)
{ pos = i; }
if(pos>j)
{ dp[i][j] += (double)dp[i-1][i-pos+j]/m; }
else
{ dp[i][j] += (double)dp[i-1][j-pos]/m; }
}
}
}
for(re int i=1; i<=n; i++)
{ printf("%.2lf%% ",dp[n][i]*100); }
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 11
设\(dp[i][j][0]\)表示到第\(i\)节课,一共换了\(j\)次,第\(i\)节课没换,所耗费的体力值的最小期望值
\(dp[i][j][1]\)表示到\(i\)节课,一共换了\(j\)次,第\(i\)节课换了,所耗费的体力值的最小期望值
状态转移方程太长了,懒得打了,直接去看代码里的吧,还是挺好理解的
最后的答案
预处理两点间最短路径时,因为点数很少,所以直接\(floyd\)就可以了
Code:
#include<cstdio>
#define MAX 2001
#define re register
#define top 11451419
namespace OMA
{
int n,m,v,e;
double way[MAX][MAX];
int c[MAX],d[MAX];
double ans=top,k[MAX];
double dp[MAX][MAX][2];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline double min(double a,double b)
{ return a<b?a:b; }
void begin()
{
for(re int i=0; i<=n; i++)
{
for(re int j=0; j<=m; j++)
{ dp[i][j][0] = dp[i][j][1] = top; }
}
for(re int i=1; i<=v; i++)
{
for(re int j=1; j<=v; j++)
{ way[i][j] = top; }
}
for(re int i=1; i<=v; i++)
{ way[i][i] = 0; }
}
void in()
{
n=read(),m=read(),v=read(),e=read();
begin();
for(re int i=1; i<=n; i++)
{ c[i] = read(); }
for(re int i=1; i<=n; i++)
{ d[i] = read(); }
for(re int i=1; i<=n; i++)
{ scanf("%lf",&k[i]); }
for(re int i=1; i<=e; i++)
{
int u=read(),v=read();
way[u][v] = way[v][u] = min(way[u][v],read());
//printf("%lf\n",way[v][u]);
}
}
void floyd()
{
for(re int k=1; k<=v; k++)
{
for(re int i=1; i<=v; i++)
{
for(re int j=1; j<=v; j++)
{ way[i][j] = min(way[i][j],way[i][k]+way[k][j]); }
}
}
}
signed main()
{
in();
floyd();
dp[1][0][0] = dp[1][1][1] = 0;
for(re int i=2; i<=n; i++)
{
for(re int j=0; j<=min(i,m); j++)
{
double a,b;
if(j)
{
a = dp[i-1][j-1][0]+k[i]*way[c[i-1]][d[i]]+(1-k[i])*way[c[i-1]][c[i]];
b = dp[i-1][j-1][1]+k[i-1]*k[i]*way[d[i-1]][d[i]]+(1-k[i-1])*(1-k[i])*way[c[i-1]][c[i]]+k[i-1]*(1-k[i])*way[d[i-1]][c[i]]+k[i]*(1-k[i-1])*way[d[i]][c[i-1]];
dp[i][j][1] = min(dp[i][j][1],min(a,b));
}
a = dp[i-1][j][0]+way[c[i]][c[i-1]];
b = dp[i-1][j][1]+k[i-1]*way[d[i-1]][c[i]]+(1-k[i-1])*way[c[i]][c[i-1]];
dp[i][j][0] = min(dp[i][j][0],min(a,b));
}
}
for(re int i=0; i<=m; i++)
{ ans = min(ans,min(dp[n][i][0],dp[n][i][1])); }
printf("%.2lf",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
Problem 12
Problem 13
树形dp