zoukankan      html  css  js  c++  java
  • Stone Game(思维、模拟)

    题意

    (n)堆石子,每堆有(a_i)个,并且相邻两堆石子的个数互不相同。

    两个人轮流取石子,每次取(1)个。取石子的过程中不能打破相邻两堆石子个数不同的规则。

    无法再取时,游戏终止。问先手必胜还是后手必胜。

    注意:当某一堆个数是(0)时,也算是一堆

    数据范围

    (1 leq T leq 100)

    (1 leq n leq 10^5)

    (0 leq a_i leq 10^9)

    思路

    这道题表面看上去是个博弈,其实稍微转化一下,发现其实就是个模拟题。

    可以先考虑一下两堆石子个数不同的规则,这个规则的含义其实就是取石子的过程中,相邻两堆石子数量的大小关系不变。

    我们要考虑什么时候游戏结束。不妨设(f(x))为石子个数函数,我们寻找(f(x))的极小值。

    设极小值是(I = {i_1, i_2, dots, i_k}),对(forall i in I),令(f(i) = 0, f(i + 1) = f(i - 1) = 1, dots, f(i + k) = f(i - k) = k),依此类推。

    最后的答案就是(sum_{i = 1}^n (a_i - f(i)))

    注意两端的石子要特判

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100010;
    
    int n;
    ll a[N];
    int loc[N];
    int cnt;
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for(int cas = 1; cas <= T; cas ++) {
            scanf("%d", &n);
            cnt = 0;
            ll sum = 0;
            for(int i = 1; i <= n; i ++) {
                scanf("%lld", &a[i]);
                sum += a[i];
            }
            if(n == 1) {
                if(sum % 2) printf("Case %d: Alice
    ", cas);
                else printf("Case %d: Bob
    ", cas);
                continue;
            }
            for(int i = 2; i < n; i ++) {
                if(a[i] < a[i - 1] && a[i] < a[i + 1]) {
                    loc[cnt] = i;
                    cnt ++;
                }
            }
            a[0] = -1;
            int r = 1;
            while(r < n && a[r] < a[r + 1]) {
                a[r] = a[r - 1] + 1;
                r ++;
            }
            a[n + 1] = -1;
            int l = n;
            while(l > 1 && a[l] < a[l - 1]) {
                a[l] = a[l + 1] + 1;
                l --;
            }
            for(int i = 0; i < cnt; i ++) {
                int t = loc[i];
                a[t] = 0;
                int l = t - 1, r = t + 1;
                while(r < n &&a[r] < a[r + 1]) {
                    a[r] = a[r - 1] + 1;
                    r ++;
                }
                while(l > 1 && a[l] < a[l - 1]) {
                    a[l] = a[l + 1] + 1;
                    l --;
                }
            }
            for(int i = 1; i <= n; i ++) {
                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 - 1] < a[i] && a[i] > a[i + 1]) a[i] = max(a[i - 1], a[i + 1]) + 1;
            }
            for(int i = 1; i <= n; i ++) sum -= a[i];
            if(sum % 2) printf("Case %d: Alice
    ", cas);
            else printf("Case %d: Bob
    ", cas);
        }
        return 0;
    }
    
  • 相关阅读:
    使用jaxb用xsd生成java类
    EMF保存CDATA
    windows builder里面的可伸缩面板
    使用eclipse open type对话框
    eclipse中toolbar位置的系统URI
    bzoj 4414 数量积 结论题
    bzoj 4402 Claris的剑 组合数学
    bzoj 4206 最大团 几何+lis
    bzoj 3676 [Apio2014]回文串 回文自动机
    bzoj 3670 [Noi2014]动物园 kmp
  • 原文地址:https://www.cnblogs.com/miraclepbc/p/14381557.html
Copyright © 2011-2022 走看看