概率与期望
A.绿豆蛙的归宿
\(f[x]\) 表示从x走到终点经过的路径的期望长度。
从 \(x\) 出发经过 \(k\) 条边,则有:
由于\(f[N]=0\),所以从终点出发在反图上跑拓扑排序。
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
const int N=200010;
int to[N],edge[N],nxt[N],head[N],in[N],out[N];
int n,m,x,y,z,tot;
double dis[N];
queue<int> q;
void add(int x,int y,int z){
to[++tot]=y;
edge[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
add(y,x,z);
in[x]++;
out[x]++;
}
q.push(n);
while(q.size()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
dis[y]+=(dis[x]+edge[i])/in[y];
out[y]--;
if(!out[y]) q.push(y);
}
}
printf("%.2f\n",dis[1]);
return kw;
}
B.扑克牌
\(f[a][b][c][d][x][y]\) 表示从当前状态跳到终点的期望长度,\(a\) 张黑桃,\(b\) 张红桃,\(c\) 张梅花,\(d\) 张方块, \(x\) 为大王状态,\(y\) 为小王状态;
状态转移有:
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
int A,B,C,D;
const int N=15;
const double inf=1e20;
double f[N][N][N][N][5][5];
double dp(int a,int b,int c,int d,int x,int y){
double &ans=f[a][b][c][d][x][y];
if(ans>=0) return ans;
int as=a+(x==0)+(y==0);
int bs=b+(x==1)+(y==1);
int cs=c+(x==2)+(y==2);
int ds=d+(x==3)+(y==3);
if(as>=A&&bs>=B&&cs>=C&&ds>=D) return ans=0;
int sum=a+b+c+d+(x!=4)+(y!=4);
sum=54-sum;
if(sum<=0) return ans=inf;
ans=1;
if(a<13) ans+=(13.0-a)/sum*dp(a+1,b,c,d,x,y);
if(b<13) ans+=(13.0-b)/sum*dp(a,b+1,c,d,x,y);
if(c<13) ans+=(13.0-c)/sum*dp(a,b,c+1,d,x,y);
if(d<13) ans+=(13.0-d)/sum*dp(a,b,c,d+1,x,y);
if(x==4){
double t=inf;
for(int i=0;i<4;i++) t=min(t,1.0/sum*dp(a,b,c,d,i,y));
ans+=t;
}
if(y==4){
double t=inf;
for(int i=0;i<4;i++) t=min(t,1.0/sum*dp(a,b,c,d,x,i));
ans+=t;
}
return ans;
}
int main(){
scanf("%d%d%d%d",&A,&B,&C,&D);
memset(f,-1,sizeof(f));
double ans=dp(0,0,0,0,4,4);
if(ans>inf/2) ans=-1;
printf("%.3lf",ans);
return kw;
}
C.聪聪和可可
这个题吧还是不太好分析整个过程的,比较麻烦。
大概意思就是猫追老鼠的问题:
猫可以走一步或两步,但猫必须走到离老鼠最近的点,并且如果有编号相同的位置,则取编号最小的点。
猫的位置在 \(i\) ,老鼠的位置在 \(j\) :
- 用 \(f[i][j]\) 表示猫抓到老鼠的期望步数;
- 猫的走位使得不得不先进行预处理,猫的下一步为 \(walk[i][j]\);
- 还需要跑最短路求出猫在 \(i\) 到达所有点的最短路径 \(dis[i][j]\) 从而预处理出猫的走位。
再进行状态转移:
- 如果 \(i = j\) ,则 \(f[i][j]=0\);
- 如果猫直接走一步或两步能够抓到老鼠,则 \(f[i][j]=1\);
- 否则的话
\(w\) 表示猫走两步到达的位置,\(z\) 表示老鼠走一步或原地不动所到达的位置,\(p[j]\) 表示点\(j\) 的出度。
最后直接记忆化搜索,答案为 \(f[cc][kk]\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,cc,kk,tot,x,y;
int p[N],to[N],head[N],nxt[N];
int dis[N][N],walk[N][N];
double f[N][N];
bool vis[N],vis1[N][N];
queue<int> q;
int read(){
int ans=0;bool f=0;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0' && ch<='9'){ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
return f?~ans+1:ans;
}
void add(int x,int y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void spfa(int x){
memset(dis[x],0x3f,sizeof(dis[x]));
dis[x][x]=0;
vis[x]=1;
q.push(x);
while(q.size()){
int top=q.front();
q.pop();
vis[top]=0;
for(int i=head[top];i;i=nxt[i]){
int y=to[i];
if(dis[x][y]>dis[x][top]+1){
dis[x][y]=dis[x][top]+1;
if(!vis[y]) vis[y]=1;
q.push(y);
}
}
}
}
double dfs(int x,int y){
if(vis1[x][y]) return f[x][y];
vis1[x][y]=1;
if(x==y) return f[x][y]=0;
int k=walk[x][y],w=walk[k][y];
if(k==y||w==y) return f[x][y]=1;
f[x][y]=1;
for(int i=head[y];i;i=nxt[i]){
int z=to[i];
f[x][y]+=dfs(w,z)/(p[y]+1);
}
f[x][y]+=dfs(w,y)/(p[y]+1);
return f[x][y];
}
int main(){
n=read(),m=read(),cc=read(),kk=read();
for(int i=1;i<=m;i++){
x=read(),y=read();
add(x,y);
add(y,x);
p[x]++,p[y]++;
}
for(int i=1;i<=n;i++) spfa(i);
memset(walk,0x3f,sizeof(walk));
for(int k=1;k<=n;k++){
for(int i=head[k];i;i=nxt[i]){
int y=to[i];
for(int j=1;j<=n;j++){
if(dis[k][j]==dis[y][j]+1){
walk[k][j]=min(walk[k][j],y);
}
}
}
}
printf("%.3lf",dfs(cc,kk));
return 0;
}
D.OSU!
对于每次贡献分数 \(x\) ++,则有:
比原分数增加的值为 \(3*x^2+3*x+1\);
所以要维护这个增加值的期望值,由于三次方并不具有线性,所以用一次方和二次方的线性关系来表示三次方,则有:
\(x^1[i]=(x^1[i-1]+1) \times chance[i]\);
\(x^2[i]=(x^2[i-1]+2 \times x^1[i-1]+1) \times chance[i]\);
\(x^3[i]=x^3[i-1]+(3 \times x^2[i-1]+3 \times x^1[i-1]+1) \times chance[i]\);
最终答案为 \(x^3[n]\)。
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
const int N=100010;
int n,res;
double chance[N],x1[N],x2[N],ans[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf",&chance[i]);
for(int i=1;i<=n;i++){
x1[i]=(x1[i-1]+1)*chance[i];
x2[i]=(x2[i-1]+2*x1[i-1]+1)*chance[i];
ans[i]=ans[i-1]+(3*(x1[i-1]+x2[i-1])+1)*chance[i];
}
printf("%.1lf",ans[n]);
return kw;
}
E.Red is good
\(f[i][j]\) 表示选择 \(i\) 张红牌,\(j\) 张黑牌的最优期望;
当翻出全是红牌,则 \(f[i][0]=1\),反之全是黑牌时,则 \(f[0][i]=0\);
状态转移:
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
const int N=5010;
int red,black;
double f[N][N];
int main(){
scanf("%d%d",&red,&black);
for(int i=1;i<=red;i++){
f[i][0]=i;
for(int j=1;j<=black;j++){
f[i][j]=max(1.0*0,1.0*i/(i+j)*(f[i-1][j]+1)+1.0*j/(i+j)*(f[i][j-1]-1));
}
}
int ans=f[red][black]*1e6;
printf("%lf",(double)ans/1e6);
return kw;
}
F.守卫者的挑战
\(i\) 为总挑战数,\(j\) 为胜利场数,\(k\) 为背包容量。
\(k>0\) 代表背包仍有剩余的空间,\(k<0\) 代表目前仍有 \(-k\) 个地图残片还未装入,由于下标不能为负数且 \(-200 \le k \le 200\),所以直接将 \(k\) 加上 \(200\) 即可。
每次共有两种决策:若第 \(i\) 次挑战胜利
若第 \(i\) 次挑战失败
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
const int N=210;
int n,l,k,a[N];
double ans,p[N],f[N][N][N*2];
int main(){
scanf("%d%d%d",&n,&l,&k);
for(int i=1;i<=n;i++){
scanf("%lf",&p[i]);
p[i]=1.0*p[i]/100;
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
f[0][0][200+min(n,k)]=1;//注意边界
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
for(int k=0;k<=400;k++){
f[i][j][k]+=f[i-1][j][k]*(1-p[i]);
if(j>0&&k-a[i]>=0) f[i][j][k]+=f[i-1][j-1][k-a[i]]*p[i];
}
}
}
for(int i=l;i<=n;i++)
for(int j=200;j<=400;j++)
ans+=f[n][i][j];
printf("%.6lf",ans);
return kw;
}
G.Easy
与C题思路大同小异
\(l[i]\) 表示枚举期望能达到连续的 \(o\) 的个数,状态转移则有:
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
const int N=3e5+10;
int n,m;
double f[N],l[N];
char s[N];
int main(){
scanf("%d",&n);
cin>>s+1;
f[0]=0;
for(int i=1;i<=n;i++){
if(s[i]=='o'){
l[i]=l[i-1]+1;
f[i]=f[i-1]+2*l[i-1]+1;
}
else if(s[i]=='x'){
f[i]=f[i-1];
}
else{
l[i]=(l[i-1]+1.0)/2.0;
f[i]=f[i-1]+l[i-1]+0.5;
}
}
printf("%.4lf",f[n]);
return kw;
}
H.单选错位
感觉比较有意思的一道思维题;
在错位的两个题中,要取选项数小的值作为概率的分子,则有:
点击查看代码
#include <bits/stdc++.h>
#define kw 0
using namespace std;
const int N=1e7+10;
int n,A,B,C,a[N];
double ans;
int main(){
scanf("%d%d%d%d%d",&n,&A,&B,&C,a+1);
for (int i=2;i<=n;i++)
a[i]=((long long)a[i-1]*A+B)%100000001;
for(int i=1;i<=n;i++)
a[i]=a[i]%C+1;
for(int i=1;i<=n-1;i++) ans+=(1.0*min(a[i],a[i+1])/(1.0*a[i]*a[i+1]));
ans+=(1.0*min(a[1],a[n])/(1.0*a[1]*a[n]));
printf("%.3lf",ans);
return kw;
}
持续更新······
浙公网安备 33010602011771号