zoukankan      html  css  js  c++  java
  • CodeChef Sereja and Game [DP 概率 博弈论]

    https://www.codechef.com/problems/SEAGM

    题意:

    n个数(可能存在相同的数),双方轮流取数。如果在一方选取之后,所有
    已选取数字的GCD变为1,则此方输。
    问:
    1 若双方均采取最优策略,先手是否必胜?
    2 若双方随机取数,先手获胜的概率为多少?
    $n,ai le 100$


    状态比较难想,核心是找到一个划分阶段的顺序:根据$GCD$划分阶段

    $GCD$是只会减小不会增加的

    课件上的状态是$f[i][j]$表示当前$GCD$为$i$,没选的$i$的倍数有$j$个,感觉有点奇怪...

    看了一下官方题解,意识到只要记录$j$为当前已经选的有$j$个就好了,已经选的一定是$i$的倍数,这样就和其他的状态比较像了

    转移还是比较好想的

    $1. f[i][j] ightarrow f[i][j+1] : j<mult[i]$

    $2. f[i][j] ightarrow f[gcd(i,k)][j+1] : 1 le gcd(i,k) le i$

    记忆化搜索倒推就行了

    PS:给$gcd$加上记忆化之后$0s$就跑过去了....

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N=105;
    const double eps=1e-8;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    int n,a[N];
    int g[N][N];
    int gcd(int a,int b){return g[a][b] ? g[a][b] : g[a][b]=(b==0?a:gcd(b,a%b));}
    //int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
    int f[N][N];
    double p[N][N];
    bool dfsWin(int u,int c){//printf("dfsWin %d %d
    ",u,c);
        int &re=f[u][c];
        if(c==n) re=0;//has chosen all
        if(u==1) re=1;//win
        if(re!=-1) return re;
    
        re=0;
        int mult=0;
        for(int i=1;i<=n;i++) if(gcd(u,a[i])==u) mult++;
        if(c<mult&&!dfsWin(u,c+1)) re=1;
        else{
            for(int i=1;i<=n;i++) 
                if(gcd(u,a[i])>1&&gcd(u,a[i])!=u) 
                    if(!dfsWin(gcd(u,a[i]),c+1)) {re=1;break;}
        }
        return re;
    }
    double dfsPro(int u,int c){//printf("dfsPro %d %d
    ",u,c);
        double &re=p[u][c];
        if(c==n) re=0.0;
        if(u==1) re=1.0;
        if(re>-0.9) return re;
    
        re=0.0;
        int mult=0;
        for(int i=1;i<=n;i++) if(gcd(u,a[i])==u) mult++;
        if(c<mult) re+= (double)(mult-c) / (n-c) * (1-dfsPro(u,c+1));
        for(int i=1;i<=n;i++)
            if(gcd(u,a[i])>1&&gcd(u,a[i])!=u)
                re+=(double)1 / (n-c) *(1-dfsPro(gcd(u,a[i]),c+1));
        if(abs(re)<eps) re=0;
        return re;
    }
    int main(){
        freopen("in","r",stdin);
        int T=read();
        while(T--){
            n=read(); int g=0;
            for(int i=1;i<=n;i++) a[i]=read(),g=gcd(a[i],g);
            if(g>1){printf("%d %.4lf
    ",n&1,double(n&1));continue;}
            
            for(int i=0;i<N;i++) for(int j=0;j<N;j++) f[i][j]=-1,p[i][j]=-1.0;
            int flag=dfsWin(0,0);
            printf("%d ",flag);
    
            double prob=dfsPro(0,0);
            printf("%.4lf
    ",prob);
        }
    }
  • 相关阅读:
    [工具推荐]005.Axure RP Pro 7.0模拟C#TAB控件
    [安卓基础] 008.Android中的显示单位
    [JavaWeb基础] 008.Spring初步配置
    [批处理教程之Shell]002.Linux 常用命令大全
    [注]新手学习编程的最佳方式是什么?
    [C#打包部署教程]001.VS2012 + InstallShield
    [站点推荐]001.学习新技能的37个最佳网站(The 37 Best Websites To Learn Something New)
    程序员如何像写代码一样找女朋友
    [工具-006] C#如何模拟发包登录
    [Linux系统] (1)常用操作(CentOS 7.x)
  • 原文地址:https://www.cnblogs.com/candy99/p/6504340.html
Copyright © 2011-2022 走看看