期望DP

联赛前继续刷DP,RT

T1 M

期望DP推公式的一个套路题。
定义 Fi 为从 i 点开始到逃脱的期望步数。
那么可得
Fi=kiF1+0ei+1kieiDegi(1+Fj)((i,j)E)
Fi=1kiei+kiF1+1kieiDegiFj
然后要弄一个树状结构,则化为
Fi=1kiei+kiF1+(ikiei)FsonDegi+(1kiei)FfaDegi
Fi=AiF1+BiFfa+Ci
对于每一个叶节点有
Fi=1kiei+kiF1+(1kiei)Ffa ,即 Ai=ki Bi=1eiki Ci=1kiei
然后假设求出了所有子节点的 A,B,C ,代入上式
Fi=1kiei+kiF1+(1kiei)FfaDegi+1kieiDegi(F1Ason+FiBson+Cson)
(1(1kiei)BsonDegi)Fi=(ki+(1kiei)AsonDegi)F1+(1kiei)CsonDegi+1kiei+1kieiDegiFfa
然后除回去就可以得到当前节点的 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
2 1,3
3 1,2,3
取最小的位置去走。
然后求停下来的步数。
显然,如果令 i 能走到j
[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值