zoukankan      html  css  js  c++  java
  • upc组队赛17 Stone Game【极小值】

    Stone Game

    题目链接

    题目描述

    Alice and Bob are always playing game! The game today is about taking out stone from the stone piles in turn.
    There are n piles of stones, and the i-th pile contains A[i] stones.
    As the number of stones in each pile differs from its neighbor’s, they determine to take out exactly one stone from one of them in one turn without breaking that property. Alice goes first.
    The player who cannot take stone will lose the game.
    Now you have to determine the winner given n numbers representing the piles, if they both play optimally.
    You should notice that even a pile of 0 stone is still regarded as a pile!

    输入

    The first line of input file contains an integer T (1≤T≤100), describing the number of test cases.
    Then there are 2×T lines, with every two lines representing a test case.
    The first line of each test case contains only one integer n (1≤n≤105) as described above.
    The second line of that contains exactly n numbers, representing the number of stone(s) in a pile in order.
    All these numbers are ranging in [0,109].
    It is guaranteed that the sum of n in all cases does not exceed 106.

    输出

    You should output exactly T lines.
    For each test case, print Case d: (d represents the order of the test case) first, then print the name of winner, Alice or Bob .

    样例输入

    2
    2
    1 3
    3
    1 3 1
    

    样例输出

    Case 1: Alice
    Case 2: Bob
    
    

    提示

    Sample 1:
    There is a possible stone-taking sequence: (1,3)-> (1,2)-> (0,2)-> (0,1)
    Here is an invalid sequence: (1,3)-> (1,2)-> (1,1)-> (0,1). Though it has the same result as the first sequence, it breaks that property “the number of stones in each pile differs from its neighbor’s”.

    以下题意和题解来源自https://blog.csdn.net/winter2121/article/details/89763995

    题意

    n堆石子,保证相邻两堆不等。A和B玩游戏,轮流从这n堆中,任意拿走一颗石子,但需要保证拿走第i堆的一颗石子后,第i堆的石子不能和他相邻堆相等。谁无法拿石子时,谁就输。问谁能赢?

    题解

    设a[x]与a[y]相邻,若a[x]>a[y],则永远不会出现a[x]<=a[y]的情况。于是整个序列的单调性不会发生变化。
    找出所有的极小值点(两端特判),这些点最后一定能拿成0。然后从极小值点往两边爬,爬到封顶,爬的时候让a[i]变成前一个+1即可。也就是保持单调性的前提下,尽量降低序列的值。  令sum = 原序列的和 - 现序列的和,则sum为奇数时A赢,否则B赢

    原博主的题解写的很好,我在原博主的基础上精简了部分代码,增添了些注释方便理解

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,a,n) for(int i=a;i<n;i++)
    #define scac(x) scanf("%c",&x)
    #define sca(x) scanf("%d",&x)
    #define sca2(x,y) scanf("%d%d",&x,&y)
    #define sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
    #define scl(x) scanf("%lld",&x)
    #define scl2(x,y) scanf("%lld%lld",&x,&y)
    #define scl3(x,y,z) scanf("%lld%lld%lld",&x,&y,&z)
    #define pri(x) printf("%d
    ",x)
    #define pri2(x,y) printf("%d %d
    ",x,y)
    #define pri3(x,y,z) printf("%d %d %d
    ",x,y,z)
    #define prl(x) printf("%lld
    ",x)
    #define prl2(x,y) printf("%lld %lld
    ",x,y)
    #define prl3(x,y,z) printf("%lld %lld %lld
    ",x,y,z)
    #define mst(x,y) memset(x,y,sizeof(x))
    #define ll long long
    #define LL long long
    #define pb push_back
    #define mp make_pair
    #define P pair<double,double>
    #define PLL pair<ll,ll>
    #define PI acos(1.0)
    #define eps 1e-6
    #define inf 1e17
    #define mod 1e9+7
    #define INF 0x3f3f3f3f
    #define N 1005
    const int maxn = 1e6+10;
    int a[maxn],b[maxn];
    int t,n;
    int kase = 0;
    int  main()
    {
        sca(t);
        while(t--)
        {
          sca(n);
          ll sum = 0;
          rep(i,1,n+1)
          {
            sca(a[i]);
            sum+=a[i];
          }
          printf("Case %d: ",++kase);
          if(n == 1) //如果只有一个数
          {
            puts(sum % 2 ? "Alice" : "Bob");
            continue;
          }
          int cnt = 0;
          a[0]= a[n+1] = -1; //方便下面操作
          //先处理单调的两端,这样后面从中间开始的操作才是正确的 
          for(int i = 1;i < n && a[i]<a[i+1]; i++) a[i] = a[i-1] + 1;
          for(int i = n;i > 1 && a[i]<a[i-1]; i--) a[i] = a[i+1] + 1;
          rep(i,2,n)
          {
            if(a[i] <a[i-1] && a[i]<a[i+1])//位于中间的极小值
            {
              a[i] = 0; //直接把他变0;
              for(int j = i-1; j > 1 && a[j] < a[j-1]; j--) //如果保持单调
                a[j] = a[j+1] + 1; 
              for(int j = i+1; j < n && a[j] < a[j+1]; j++)
                a[j] = a[j-1] + 1;
            }
          }
          rep(i,1,n+1)
          { //因为在上面的操作中我们没有处理头和尾,所以在这里处理
            if(i == 1 && a[1] > a[2]) a[1] = a[2] + 1; 
            else if(i == n && a[n] > a[n-1]) a[n] = a[n-1]+1;
            else if(a[i] > a[i-1] && a[i] > a[i+1])
              a[i] = max(a[i-1],a[i+1])+1; //如果比旁边两个都大,那只能减到大者+1的数
          }
    
          rep(i,1,n+1)
          {
            sum-=a[i];//操作次数 = 原序列-操作后序列
            //printf("%d ",a[i]);
          }
          //puts("");
          //prl(sum);
          puts(sum % 2 ? "Alice" : "Bob");
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    OpenFileMapping
    findwindow
    CopyMemory
    SetWindowsHookEx
    fillchar
    什么是ashx文件
    WPF中的控件
    WPF X名称空间里都有什么
    XAML语法学习之...
    Repeater控件使用总结
  • 原文地址:https://www.cnblogs.com/llke/p/10822139.html
Copyright © 2011-2022 走看看