联赛前继续刷DP,RT
T1 M
期望DP推公式的一个套路题。
定义
Fi
为从
i
点开始到逃脱的期望步数。
那么可得
Fi=1−ki−ei+ki∗F1+1−ki−eiDegi∑Fj
然后要弄一个树状结构,则化为
Fi=1−ki−ei+ki∗F1+(i−ki−ei)∑FsonDegi+(1−ki−ei)FfaDegi
令
Fi=Ai∗F1+Bi∗Ffa+Ci
对于每一个叶节点有
Fi=1−ki−ei+ki∗F1+(1−ki−ei)Ffa
,即
Ai=ki
,
Bi=1−ei−ki
,
Ci=1−ki−ei
然后假设求出了所有子节点的
A,B,C
,代入上式
Fi=1−ki−ei+ki∗F1+(1−ki−ei)FfaDegi+1−ki−eiDegi(F1∗∑Ason+Fi∗∑Bson+∑Cson)
(1−(1−ki−ei)∑BsonDegi)Fi=(ki+(1−ki−ei)∑AsonDegi)F1+(1−ki−ei)∑CsonDegi+1−ki−ei+1−ki−eiDegiFfa
然后除回去就可以得到当前节点的
A,B,C
了。
这样DFS整棵树即可求得答案。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define DB double
#define M 10004
#define EPS 1e-10
int T,n;
int Next[M<<1],V[M<<1],Head[M],tot,Deg[M];
void Add_Edge(int u,int v){
Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
DB e[M],k[M];
DB A,B,C;
void DFS(int X,int f){
DB a,b,c,p;a=b=c=0;
int Y;
LREP(i,X)if((Y=V[i])!=f)
DFS(Y,X),a+=A,b+=B,c+=C;
p=(1-k[X]-e[X])/Deg[X];
A=(k[X]+p*a)/(1-p*b);
B=p/(1-p*b);
C=(1-k[X]-e[X]+p*c)/(1-p*b);
}
int main(){
scanf("%d",&T);
REP(Case,1,T+1){
memset(Head,tot=0,sizeof(Head));
memset(Deg,0,sizeof(Deg));
scanf("%d",&n);
REP(i,1,n){
int u,v;
scanf("%d%d",&u,&v);
Add_Edge(u,v);
Add_Edge(v,u);
Deg[u]++;
Deg[v]++;
}
REP(i,1,n+1)scanf("%lf%lf",&k[i],&e[i]),k[i]/=100,e[i]/=100;
printf("Case %d: ",Case);
A=B=C=0;
DFS(1,0);
if(fabs(A-1)<EPS)puts("impossible");
else printf("%.6lf\n",C/(1-A));
}
return 0;
}
T2 W5X
抽象一下题意就是
从
1
到
从
2
到
从
3
到
取最小的位置去走。
然后求停下来的步数。
显然,如果令
i
能走到
则
[i+A,j)
必须为i无法走到的。
使用期望值的线性定义,求出到达每个点为每种情况的概率。
然后就可以算了。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define M 2004
#define DB double
int T,n,A,B;
DB P0[M],P1[M],P2[M],P3[M];
DB F1[M],F2[M],F3[M];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&A,&B);
REP(i,1,n+1)
scanf("%lf%lf%lf%lf",&P0[i],&P1[i],&P2[i],&P3[i]);
memset(F1,0,sizeof(F1));
memset(F2,0,sizeof(F2));
memset(F3,0,sizeof(F3));
F1[0]=F2[0]=P0[n+1]=P1[n+1]=P2[n+1]=0;
F3[0]=P3[n+1]=1;
DB Ans=0;
REP(i,0,n+1){
if(i+A>n){Ans+=F1[i]+F2[i]+F3[i];continue;}
// cerr<<i<<":"<<F1[i]<<' '<<F2[i]<<' '<<F3[i]<<endl;
REP(j,i+A,i+B+1){
if(j>n+1)break;
F2[j]+=P2[j]*F1[i];
F3[j]+=P3[j]*F1[i];
F1[j]+=P1[j]*F2[i];
F3[j]+=P3[j]*F2[i];
F1[j]+=P1[j]*F3[i];
F2[j]+=P2[j]*F3[i];
F3[j]+=P3[j]*F3[i];
Ans+=P2[j]*F1[i]
+P3[j]*F1[i]
+P1[j]*F2[i]
+P3[j]*F2[i]
+P1[j]*F3[i]
+P2[j]*F3[i]
+P3[j]*F3[i];
F1[i]*=P0[j]+P1[j];
F2[i]*=P0[j]+P2[j];
F3[i]*=P0[j];
}
}
printf("%.8lf\n",Ans);
}
return 0;
}
T3 E
期望值的另一种求法。
即求对于每个门需要炸弹的事件发生的概率。
这个东西可以用bitset优化,复杂度诡异。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
#define DB double
#define M 1004
int T,n;
bitset<M>B[M];
int main(){
scanf("%d",&T);
REP(Case,1,T+1){
scanf("%d",&n);
REP(i,1,n+1)B[i].reset();
REP(i,1,n+1){
int w,x;
scanf("%d",&w);
REP(j,0,w){
scanf("%d",&x);
B[i][x]=1;
}
B[i][i]=1;
}
REP(j,1,n+1)
REP(i,1,n+1)
if(B[i][j])
B[i]|=B[j];
DB Ans=0;
REP(i,1,n+1){
int Tmp=0;
REP(j,1,n+1)
Tmp+=B[j][i];
Ans+=1.0/Tmp;
}
printf("Case #%d: %.5lf\n",Case,Ans);
}
return 0;
}
T4 C
相差不超过1,
说明这些学生做题是一组一组做的,每组为n。
然后做很多次状压DP即可。
为了方便一些可以先把m补齐到n的倍数。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
#define DB double
#define M 1044
int T,n,m,Num[M];
DB P[11][M],F[M];
int main(){
REP(i,1,1<<10)REP(j,0,10)if(i&(1<<j))Num[i]++;
scanf("%d",&T);
REP(Case,1,T+1){
scanf("%d %d",&n,&m);
REP(i,0,n)REP(j,1,m+1)scanf("%lf",&P[i][j]);
DB Ans=0;
while(m%n){
m++;
REP(i,0,n)P[i][m]=1;
Ans-=1;
}
int i=1,S=(1<<n)-1;
while(i<m){
memset(F,0,sizeof(F));
REP(j,0,S) REP(k,0,n)if(!(j&(1<<k)))
chkmax(F[j|(1<<k)],F[j]+P[k][i+Num[j]]);
Ans+=F[S];
i+=n;
}
printf("Case #%d: %.5lf\n",Case,Ans);
}
return 0;
}