• 数学3(博弈+splya)


    数学3(博弈+splya)

    标签: 数学


    hdu_5194 (打表找规律)

    • 题意

    有n和黑球和m个白球,现在一个个的取出这些球,如果是黑球则当前标记为1,白球为0,那么当取完这些球会得到一些序列。问你在所有的序列中出现01这个子序列的期望

    • 题解

    首先要掌握一种技能就是可以通过打表找规律的方式解题,因为真的有很多的题都是这么解出来的。打表有一个很好的习惯
    如果用手打表的话,最好列出一个n*m的表格,将对应的n和m的值填入对应的格子,这样最后比较容易可以看出规律这个题就是打表做的,规律是什么直接看代码吧

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int gcd(int a, int b)
    {
        return b==0?a:gcd(b,a%b);
    }
    int main()
    {
        int n,m;
        while(~scanf("%d%d",&n,&m))
        {
            int fm = n+m;
            int fz = n*m;
            int tm = gcd(fz,fm);
            printf("%d/%d
    ",fz/tm,fm/tm);
        }
        return 0;
    }
    
    

    hdu_5366 (dp)

    • 题意

    要在一个1~N的区间里,放置一种东西,每个东西占据一个单位区间,要求两个东西之间的距离大于等于2,问你有几种放法

    • 题解

    这个是一个很典型的dp问题
    dp[i] 表示放置到第i个位置的时候的放置种类数
    那么有第i个位置可以放一个,其他的位置都不放,那么多出这个位置就多出了一种情况,还有就是如果这一位不放那么就是dp[i-1],如果放的话,就是dp[i-3]+1;
    (dp[i] = dp[i-1]+1+dp[i-3])
    很简单吧,做题的时候相信自己可以做出来就可以肆意ac了

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    ll dp[66];
    void init()
    {
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for(int i = 4; i < 66 ; i++)
        {
            dp[i] = dp[i-1]+1+dp[i-3];
        }
    }
    int main()
    {
        int n;
        init();
        while(~scanf("%d",&n))
        {
            printf("%lld
    ",dp[n]);
        }
        return 0;
    }
    
    

    好了,下面要开始学习一下博弈了
    注意,博弈的核心是sg还有打表,模拟找规律

    hdu_3951 一个简单的博弈

    • 题意

    有一圈数字收尾相连,你可以拿编号相连的数字,注意,头尾的数字也可以看成是相连的。每次只能拿1~k的个数个数字,最后不能拿得输掉

    • 题解

    其实有很多的博弈都是根据对称性,你可以考虑一下,这个题,首先,如果(k>n)的时候就可以第一个人都取完,那么第二个人输掉了
    再考虑到如果k = 1的话,直接根据n的奇偶判断就可以了
    然后如果n是偶数的话, 第一个人不能取完,那么考虑,如过第一个人拿了超过一半的话,第二个人肯定可以一次性把剩下的都拿走,所以第二个人赢。如果不拿走一半,那么第二个人永远可以模仿着第一个人,在对称的位置上再拿取相同数目的数字,所以仍然是2赢
    考虑n是奇数,相同的分析方式可以得出结论就是2赢

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int main()
    {
        int T;
        scanf("%d",&T);
        int cnt = 1;
        while(T--)
        {
            int n,k;
            scanf("%d%d",&n,&k);
            printf("Case %d: ",cnt++);
            if(k>=n) puts("first");
            else if(k==1){
                if(n%2==1) puts("first");
                else puts("second");
            }
            else puts("second");
        }
        return 0;
    }
    
    

    hdu_1847 sg

    • 题解

    介绍一下sg函数:

    定义: 一个状态的sg是它所有的后继状态的所有sg中第一个没有出现的非负整数。
    用法: sg函数为0可以指示一个状态,不为0为一个状态,所有石子堆的sg的异或值等于整个石子堆的异或值。
    求法:递归的求就可以了,对于有n堆石子,每次拿取的个数有一定的个数,直接用模板就可以。
    sg在比赛的时候还可以打表找规律哈哈。这点很重要

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N = 1008;
    int sg[N];
    int s[11];
    int Getsg(int n,int k)
    {
        int mex[1008];
        memset(mex,0,sizeof(mex));
        if(sg[n]!=-1) return sg[n];
        for(int i = 0; i < k && n-s[i]>=0; i++){
            mex[Getsg(n-s[i],k)] = 1;
        }
        for(int i = 0; ; i++){
            if(!mex[i]){
                return sg[n] = i;
            }
        }
    }
    int main()
    {
        int n;
        memset(sg,-1,sizeof(sg));
        sg[0] = 0;
        for(int i = 0; i < 11; i++){
            s[i] = pow(2.0,(double)i);
        }
        while(~scanf("%d",&n))
        {
            if(Getsg(n,11)!=0) puts("Kiki");
            else puts("Cici");
        }
        return 0;
    }
    
    

    hdu_1536 sg

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int s[108];
    int h[108];
    int sg[10008];
    int Getsg(int n,int k)
    {
        int mex[108];//注意一定要写在里面。请仔细思考为什么这么做。
        memset(mex,0,sizeof(mex));
        if(sg[n]!=-1) return sg[n];//因为集合s一致,则每个数量的mex一样,可以重复利用
        if(n-s[0]<0) return sg[n] = 0;//s[0]为集合中最小值,n-s[0]<0,则n不可能到达其他状态,一定为P
        for(int i = 0; i < k && n-s[i]>=0; i++){
            mex[Getsg(n-s[i],k)] = 1;//n的后继SG函数中的没有出现的最小非负数
        }
        for(int i = 0; ; i++){
            if(!mex[i]){
               //printf("i = %d
    ",i);
                return sg[n] = i;
            }
        }
    }
    int main()
    {
        int k;
        while(~scanf("%d",&k),k)
        {
            memset(sg,-1,sizeof(sg));
            sg[0] = 0;
            for(int i = 0; i < k; i++){
                scanf("%d",&s[i]);
            }
            sort(s,s+k);
            int m;
            scanf("%d",&m);
            for(int i = 0; i < m; i++)
            {
                int t;
                scanf("%d",&t);
                int fl = 0;
                for(int j = 0; j < t; j++){
                    scanf("%d",&h[j]);
                    fl = fl ^ Getsg(h[j],k);
                }
                if(fl==0) printf("L");
                else printf("W");
            }
            printf("
    ");
        }
        return 0;
    }
    //http://blog.csdn.net/liwen_7/article/details/7943258
    
    

    hdu_2516 斐波那契博弈

    • 题解

    斐波那契博弈的固定描述

    有一堆个数为n的石子,游戏双方轮流取石子,满足:

    1)先手不能在第一次把所有的石子取完;

    2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。

    约定取走最后一个石子的人为赢家,求必败态。

    如果当前的数是斐波那契数列中的一个数的话,为一种状态,不是为另一种状态

    • 代码
    //斐波那契博弈
    //http://blog.csdn.net/acm_cxlove/article/details/7835016
    //一般斐波那契数列求到第45个就够了
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 45;
    #define ll long long
    
    ll f[N];
    void init()
    {
        memset(f,0,sizeof(f));
        f[0] = 1;
        f[1] = 2;
        for(int i = 2; i <= 45; i++){
            f[i] = f[i-1]+f[i-2];
        }
    }
    
    int main()
    {
        ll n;
        init();
        while(~scanf("%lld",&n),n)
        {
            bool fl = 0;
            for(int i = 0; i <= 45; i++){
                if(f[i]==n){
                    fl = 1;
                    break;
                }
            }
            if(fl) puts("Second win");
            else puts("First win");
        }
        return 0;
    }
    
    

    poj_1286 polya

    介绍一下burnside 和 polya

    结论:
    burnside:
    对于一个置换f,若一个着色方案s经过置换后不变,称s为f的不动点,将f的不动点的个数记为 (C(f)) 则等价类数目为所有 (C(f)) 的平均值
    polya:
    如果置换f分解成 (m(f)) 个循环的乘积,假设k种染色,则有 $C(f) = k^{m(f)} $ 带入burnside,等价类的个数等于所有的置换f的$C(f) = k^{m(f)} $ 的平均数

    应用:
    所以做题的时候要找到所有的置换
    置换要构成置换群,即满足所有置换F中任意两个置换的乘积也应当在F中
    然后对于每个置换找到其不动点个数,或者是找到循环节的个数然后带入公式即可

    • 题意

    就是一个手镯,用三种颜色图,可以旋转和翻转,求有多少方案。

    • 题解

    对于旋转的话有n种置换,每种置换的循环节为gcd(i,n),i是旋转的个数,可以自己找找规律
    对于翻转的置换
    考虑n是奇数,则对于每个点都有一个对称轴,那么就有n种置换,每种置换的循环节是(n+1)/2
    如果n是偶数,那么有n/2中置换是点通过对称轴对应的循环节为(n/2)+1;
    有n/2种置换是对称轴不通过点,那么有n/2个循环节

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define ll long long
    ll gcd(ll a, ll b)
    {
        return b==0?a:gcd(b,a%b);
    }
    ll Polya(ll n)
    {
        if(n==0)
            return 0;
        //旋转
        ll c;
        ll sum = 0;
        for(ll i = 1; i <= n; i++)
        {
            c = gcd(i,n);
            sum+=pow(3.0,(double)c);
        }
        //翻转
        if(n%2==0)
        {
            sum+=pow(3.0,(double)((n+2)/2))*n/2;
            sum+=pow(3.0,(double)n/2)*n/2;
        }
        else
        {
            sum+=pow(3.0,(double)((n+1)/2))*n;
        }
        return sum/(2*n);
    }
    int main()
    {
        ll n;
        while(~scanf("%lld",&n))
        {
            if(n==-1) return 0;
            printf("%lld
    ",Polya(n));
        }
        return 0;
    }
    
    

    poj_2369 置换

    • 题意

    给定一个序列,问需要最少需要置换多少次才能变为有序序列.

    *分析

    对于每一位,算出最少的置换到自己应该的数字。每一位都有这样的数字,取最小公倍数就可以。对于置换的题一般都去想循环节

    • 代码
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    using namespace std;
    #define ll long long
    map <ll,ll> m;
    bool vis[1004];
    ll gcd(ll a, ll b)
    {
        return b==0?a:gcd(b,a%b);
    }
    ll lcd(ll a, ll b)
    {
        return a/gcd(a,b)*b;
    }
    int main()
    {
        ll n;
        while(~scanf("%lld",&n))
        {
            m.clear();
            ll tm;
            for(ll i = 1; i <= n; i++)
            {
                scanf("%lld",&tm);
                m[i] = tm;
                vis[i] = 0;
            }
            ll c;
            ll ans = 1;
            for(ll i = 1; i <= n; i++){
                if(vis[i]!=0){
                    continue;
                }
                c = 0;
                ll tt = i;
                vis[tt] = 1;
                while(1)
                {
                    c++;
                    //printf("tt = %lld
    ",tt);
                    tt = m[tt];
                    if(vis[tt]!=0) break;
                    vis[tt] = 1;
                }
                ans = lcd(c,ans);
               // printf("ans = %lld
    ",ans);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
    

    uva_10733 置换

    题意:求用n中颜色涂立方体的不同种数,能旋转到的算一种
    题意:和上一题UVA - 10601 Cubes (组合+置换) 的立方体旋转考虑的分类是一样的,不过这里我们考虑的是涂面的情况
    1.不变置换(1)(2)(3)(4)(5)(6), 共1个;
    2.沿对面中心轴旋转 90度, 270度 (1)(2345)(6), (1)(5432)(6) 同类共 6个;
    3.沿对面中心轴旋转 180度 (1)(24)(35)(6), 同类共 3个;
    4.沿对角线轴旋转 120度, 240度 (152)(346), (251)(643) 同类共 8个;
    5.沿对边中点轴旋转 180度 (16)(25)(43) 同类共 6个;

    #include <cstdio>
    #include <cstring>
    
    unsigned long long n;
    
    int main() {
        while (~scanf("%llu", &n) && n) {
            printf("%llu
    ", (n * n * n * n * n * n + 12 * n * n * n + 3 * n * n * n * n + 8 * n * n) / 24);
        }
        return 0;
    }
    
    
  • 相关阅读:
    如何写一个bat文件,让他去执行某一个地方的bat文件
    服务器加电自动开机模式设置
    第三章 线程状态
    第二章 线程安全
    第一章 线程
    多线程入门
    异常
    SSO系统介绍
    解决:Nginx访问静态页面出现中文乱码
    错误处理:java.lang.NoClassDefFoundError: javax/jms/JMSContext
  • 原文地址:https://www.cnblogs.com/shanyr/p/5765583.html
Copyright © 2020-2023  润新知