zoukankan      html  css  js  c++  java
  • 贪心(打表) 乱搞(暴力) 博弈(猜结论) 专场

    说在前面

    反正这些东西练了还不是不会。。。

    还有为什么我要抄课件题解啊喂

    博弈有两类题,一类是找规律,另一类也是找规律    鲁迅(雾

    CodeForces 39A     C*++ Calculations

    题目大意

      给一个单变量、常系数的C++式子
      定义模糊的部分按任意次序计算
      比如a+++++a到底先a++还是++a
      给定这个变量的初值,求最大可能值
      例子:
      5*a++-3*++a+a++
      a+++++a

    解析

      贪心,按照变量前面的系数大小来贪心,系数越小越先算。
      证明如下

      一方面,a+++++a以任意顺序算出来的结果是一样的
      另外一方面,考虑4个式子,k*++a+m*a++,k*++a+m*++a,k*a+++m*a++,k*a+++m*++a
      以k*++a+m*a++为例
      先算第一个再算第二个,k*(a+1)+m*(a+1)
      反过来,k*(a+2)+m*a,即系数越小的越先算

      证明中并没有要求a的值的正负,所以就算是负数证明也是成立的
      其实感觉这个题还好,主要是实现起来有一些麻烦。

      因为没有常数项,所以考虑按符号,系数,字母这样的顺序进行处理,与洛谷P1022的处理类似

      代码如下(丑陋如斯)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int a,ans,cnt,len;char ch[1000005];
    struct node{int k,id;}q[1000005];
    bool num(int a){return '0'<=a&&a<='9';}
    bool cmp(node a,node b){return a.k==b.k?a.id<b.id:a.k<b.k;}
    int main()
    {
        scanf("%d%s",&a,ch+1);len=strlen(ch+1);
        for(int i=1;i<=len;i++)
        {
            if(ch[i]=='a')
            {
                if(i==1||ch[i-1]=='+')q[++cnt]=(node){1,1};
                if(ch[i-1]=='-')q[++cnt]=(node){-1,1};i+=3;continue;
            }
            if(ch[i]=='+')
            {
                if(i==1||ch[i-1]=='+')q[++cnt]=(node){1,0};
                if(ch[i-1]=='-')q[++cnt]=(node){-1,0};i+=3;continue;
            }
            if(num(ch[i]))
            {
                int n=0,be=i;
                while(num(ch[i]))n=n*10+ch[i]-'0',i++;i++;
                if(ch[i]=='a')
                {
                    if(be==1||ch[be-1]=='+')q[++cnt]=(node){n,1};
                    if(ch[be-1]=='-')q[++cnt]=(node){-n,1};i+=3;continue;
                }
                if(ch[i]=='+')
                {
                    if(be==1||ch[be-1]=='+')q[++cnt]=(node){n,0};
                    if(ch[be-1]=='-')q[++cnt]=(node){-n,0};i+=3;continue;
                }
            }
        }
        sort(q+1,q+1+cnt,cmp);
        for(int i=1;i<=cnt;i++)
        {
            if(q[i].id==1)
                ans+=q[i].k*(a++);
            else
                ans+=q[i].k*(++a);
        }
        printf("%d
    ",ans);
    }

    CodeForces 45G     Prime Problem

    题目大意

      把1~n这n个数分成若干组,使得每组内的数的和是质数。任何一种分法都可以要求组数最少
      n ≤ 6000

    解析

      构造
      用哥德巴赫猜想(小范围的正确性?),一个大于2的偶数可以被拆分成两个质数的和

     
      1~n的和为质数,只用分一组
      1~n的和为偶数,拆成两个质数
      1~n的和为奇数,拆成3+两个质数或者2+质数
      显然是组数最少的分法

      然而一开始我忘了哥德巴赫猜想是个什么

      代码

     

    #include<cstdio>
    int n,sum,col[6005];
    bool pan(int a){for(int i=2;i*i<=a;i++)if(a%i==0)return 0;return 1;}
    int main()
    {
        scanf("%d",&n);sum=n*(n+1)/2;
        if(pan(sum)){for(int i=1;i<=n;i++)printf("1%c",i==n?'
    ':' ');return 0;}
        if(sum%2)
        {
            if(pan(2)&&pan(sum-2))
            {for(int i=1;i<=n;i++)printf("%d%c",i==2?2:1,i==n?'
    ':' ');return 0;}
            int ans;
            for(int i=2;i<=n;i++)
            {
                if(i==3)continue;
                if(pan(i)&&pan(sum-3-i)){ans=i;break;}
            }
            col[3]=2;col[ans]=3;
            for(int i=1;i<=n;i++)printf("%d%c",col[i]?col[i]:1,i==n?'
    ':' ');
        }
        else
        {
            int ans;
            for(int i=2;i<=n;i++)if(pan(i)&&pan(sum-i)){ans=i;break;}
            for(int i=1;i<=n;i++)printf("%d%c",i==ans?2:1,i==n?'
    ':' ');
        }
        return 0;
    }

    OpenJ_POJ C16D     Extracurricular Sports

    题目大意

      找n个不同的数a1,…,an
      使得lcm(a1,…,an)=a1+…+an
      只需要求一个任意可行解
      n ≤ 200,ai ≤ 10^100

    解析

      发现等比数列1,p,p^2,p^3......的前n-1项和为(p^n-1)/(p-1),lcmp^(n-1)    ←说得好像高中还学了什么其他数列似的

      那我们就试着改一下等比数列,看它是否能符合要求。

      和的分母是p-1就很烦,所以考虑p2

        1,2,4,8,16,32......

       就有和为2^n-1,lcm2^(n-1)
       和那里少了一个1,就把2变成3

      1,3,4,8,16,32......

      和就变为2^n = 2*2^(n-1)lcm3*2^(n-1),少了一个2^(n-1)但可以添加一个3的因子

        再把2^(n-2)这一项乘上一个3,就可以弥补那个2^(n-1)

      这是前n-1的情况,推一下n的情况可以发现答案为

       3*2^(n-1),2^n,2^(n-2),…,4,3,1

      还有,这个题要写高精度。。。

      代码

    #include<cstdio>
    #include<cstring>
    int T,n,num[105],sp[105];
    void mul(){for(int i=1;i<=100;i++)num[i]*=2;for(int i=1;i<=100;i++)if(num[i]>9)num[i+1]+=num[i]/10,num[i]%=10;}
    void mul3(){for(int i=1;i<=100;i++)sp[i]=num[i]*3;for(int i=1;i<=100;i++)if(sp[i]>9)sp[i+1]+=sp[i]/10,sp[i]%=10;}
    void print(){for(int i=100,flag=0;i>=1;i--){flag|=(num[i]>0);if(flag)printf("%d",num[i]);}puts("");}
    void print1(){for(int i=100,flag=0;i>=1;i--){flag|=(sp[i]>0);if(flag)printf("%d",sp[i]);}puts("");}
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            memset(num,0,sizeof num);memset(sp,0,sizeof sp);num[1]=1;
            scanf("%d",&n);
            if(n==2){printf("-1
    ");continue;}if(n==3){printf("10
    20
    30
    ");continue;}
            for(int i=1;i<n;i++)
            {
                if(i==2){printf("3
    ");mul();continue;}
                if(i==n-1){mul3();mul();print();print1();continue;}
                print();mul();
            }
            puts("");
        }
    }

    POJ 3372     Candy Distribution  

    题目大意

      n个小孩围成一个圈,老师顺时针隔0,1,2,…个小孩发糖,问每个小孩是否都能领到糖。
      n ≤ 10^9

    复制粘贴课件的解析

      通过找规律可以发现,若n2的幂,那么就可以,反之不行
      证明
      设f(x)=x(x-1)/2,那么相当于证明f(1),…,f(n)构成n的完全剩余系
      即证明对于所有的i,j,f(i)!=f(j) (mod n)
      i*(i-1)/2 != j*(j-1)/2 (mod n)
      (i-j)*(i+j+1)/2 != 0 (mod n)
      i-ji+j+1的奇偶性不同,故(i-j)*(i+j+1)/2必为奇数*一个数的形式
      由i,j的任意性,只有当n2的幂的时候才成立
      应该是这些题中代码量最短的代码

    #include<cstdio>
    long long n,a[35];
    int main()
    {
        a[0]=1;
        for(int i=1;i<=30;i++)a[i]=a[i-1]*2ll;
        while(scanf("%lld",&n)!=EOF)
        {
            int flag=0;
            for(int i=0;i<=30;i++)
                flag|=(a[i]==n);
            if(flag)puts("YES");
            else puts("NO");
        }
        return 0;
    }

    HDU 4642     Fliping game

    题目大意

      一个n*m棋盘上每个格子摆放一个硬币,有正有反
      每次选择一枚正面的硬币(x,y),从(x,y)向右下角所有的硬币都被翻转
      当某个人无法翻硬币(所有硬币都是反面) ,这个人判输
      1 ≤ n,m ≤ 100

    解析

      每次最右下角的硬币都必被翻(谁一来就想得到啊喂)
      所以若一开始右下角的硬币是正面,那么后手的人一定把这个硬币翻回正面,从而让先手的人有硬币可翻
      从而这枚硬币决定了胜负。
      所以,这就是细节决定成败(雾

      代码

     

    #include<cstdio>
    long long T,n,m,cnt,a[105][105];
    int main()
    {
        scanf("%lld",&T);
        while(T--)
        {
            scanf("%lld%lld",&n,&m);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    scanf("%lld",&a[i][j]);
            if(a[n][m]==1)puts("Alice");
            else puts("Bob");
        }
        return 0;
    }

    HDU 2516     取石子游戏

    题目大意

      1堆石子有n个,两人轮流取
      先取者第1次可以取任意多个,但不能全部取完
      以后每次取的石子数不能超过上次取子数的2倍
      取完者胜
      2 ≤ n < 2^31

    一言不合抄课件解析

      找规律可知若n是斐波那契数,那么先手必败,否则必胜
      证明
      斐波那契数的情形,首先f[1]=2的情形先手必败,用归纳法
      f[n]=f[n-1]+f[n-2]
      先手不能取超过f[n-2]的石子,因为f[n-1]<2*f[n-2]
      那么由归纳假设可知一定是后手取到f[n-2]这堆石子的最后一颗
      但后手取完f[n-2]这堆石子的最后一颗后,先手并不能一下子取完f[n-1]这堆石子,由归纳假设可得也是后手取到f[n-1]这堆石子的最后一颗
      非斐波那契数的情形,由Zeckendorf定理,任何正整数可以表示为若干个不连续的斐波那契数之和
      当n不是斐波那契数时,n=f[a1]+f[a2]+…+f[ap] (p>1)
      a1>a2>…>ap
      由于不连续,所以先手的人可以一下子取完ap这堆石子
      且后手的人不能一下子取完a(p-1)这堆石子
      那么对于后手的人而言,是先手取a(p-1)这堆石子,结果是先手的人取完a(p-1)这堆石子
      所以先手必胜
      代码

    #include<cstdio>
    long long n,fib[55];
    int main()
    {
        fib[1]=fib[2]=1;for(int i=3;i<=50;i++)fib[i]=fib[i-1]+fib[i-2];
        while(scanf("%lld",&n)&&n)
        {
            int flag=0;
            for(int i=1;i<=50;i++)flag|=(n==fib[i]);
            if(flag)puts("Second win");
            else puts("First win");
        }
        return 0;
    }

     

    HDU 3032    Nim or not Nim?

    题目大意

      n堆石子,每一次可以从任意一堆中拿走任意个石子,也可以将一堆石子分为两个小堆
      先拿完者获胜
      n ≤ 10^6, 石子数量在int范围内

    解析

      sg函数和sg定理
      sg(0)=0,sg(1)=mex(sg(0))=1,sg(2)=mex(sg(0),sg(1),sg(1)^sg(1))=2
      递推对于此题的大范围是不可行的
      通过sg函数找规律,来做大范围的情况

      代码

    #include<cstdio>
    int T,i,n,x,ans;
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(ans=0,i=1;i<=n;i++)
            {
                scanf("%d",&x);
                if(x%4==3)ans^=x+1;
                if(x%4==0)ans^=x-1;
                if(x%4==1||x%4==2)ans^=x;
            }
            if(ans)puts("Alice");
            else puts("Bob");
        }
        return 0;
    }

    HDU 5754    Life Winner Bo

    题目大意

      有一枚棋子,从(1,1)到(n,m),两人轮流走,先到的赢
      棋子是以下四种类型之一:
      王:只能右、下和斜着走一格。
      皇后:和王一样,但是可以任意步。
      骑士:只能右、下,但是可以任意步。
      马:走日(右下方)。
      2 ≤ n,m ≤ 1000

    解析

      王的情形
      用sg函数打表找规律
      sg(1,1)=0
      sg(x,y)=mex(sg(x-1,y),sg(x,y-1),sg(x-1,y-1))
      后的情形
      相当于两堆物品分别有n,m个,每次从任一堆取至少一个或同时从两堆中取同样多个,最后取光者胜
      wythoff博弈
      骑士的情形
      相当于两堆物品分别有n,m个,每次从任一堆取至少一个,最后取光者胜
      nim博弈
      马的情形
      题目要求不能移动且没有到右下角时为平手
      对于当前位置,如果能走到必败态,那么一定是必胜态
      若不能走到必败态,那么如果能走到平局位置,则一定是平局

      然后。。。。打表大法好

      代码

    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int T,type,n,m;
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d",&type,&n,&m);
            if(type==1){if(n%2==1&&m%2==1)puts("G");else puts("B");}
            if(type==2){if(n^m)puts("B");else puts("G");}
            if(type==3)
            {
                if((n+m)%3!=2)puts("D");
                else if(m==n)puts("G");
                else if(abs(n-m)==1)puts("B");
                else puts("D");
            }
            if(type==4)
            {
                n--,m--;
                if(n>m)swap(n,m);int k=m-n;
                if(int(double(k)*((1.0+sqrt(5.0))/2.0))==n&&int(double(k)*((3.0+sqrt(5.0))/2.0))==m)
                puts("G");else puts("B");
            }
        }
        return 0;
    }

     

  • 相关阅读:
    JVM 参数(转)
    redis 事务
    redis 命令
    Redis配置文件参数说明(转)
    zookeeper原理(转)
    数字证书原理 转载
    证书 签名 验签 实例
    SSL双向认证java实现 (转)
    详细介绍Java垃圾回收机制 转载
    Java Socket重要参数讲解 (转载)
  • 原文地址:https://www.cnblogs.com/firecrazy/p/11435146.html
Copyright © 2011-2022 走看看