zoukankan      html  css  js  c++  java
  • [Codeforces1421E]Swedish Heroes

    CF1421E

    题意:给定长度为 (n) 的序列 (a_n),进行 (n-1) 次操作,每次选择相邻的两个数合并,合并规则是相加并取反,最大化剩下的那个数。

    SakuraiMomoka的提交 完美地揭示了此题的解题过程。

    首先容易发现最后答案是 (sum_{i = 1}^nsig_i imes a_i,sig_iin{0, 1}),即给每个 (a_i) 前面加上符号。

    但并不是所有情况都是合法的,因此先把小数据的所有合法分配符号的方案打表出来,下面的数据来自SakuraiMomoka的提交

    //~ 2
    //~ --
    //~ 3
    //~ ++-
    //~ -++
    //~ 4
    //~ ++++
    //~ +---
    //~ -+--
    //~ --+-
    //~ ---+
    //~ 5
    //~ +++--
    //~ ++-+-
    //~ ++--+
    //~ +-++-
    //~ +--++
    //~ -+++-
    //~ -++-+
    //~ -+-++
    //~ --+++
    //~ -----
    //~ 6
    //~ +++++-
    //~ ++++-+
    //~ +++-++
    //~ ++-+++
    //~ ++----
    //~ +-++++
    //~ +-+---
    //~ +--+--
    //~ +---+-
    //~ +----+
    //~ -+++++
    //~ -++---
    //~ -+-+--
    //~ -+--+-
    //~ -+---+
    //~ --++--
    //~ --+-+-
    //~ --+--+
    //~ ---++-
    //~ ---+-+
    //~ ----++
    //~ 7
    //~ +++++++
    //~ ++++---
    //~ +++-+--
    //~ +++--+-
    //~ +++---+
    //~ ++-++--
    //~ ++-+-+-
    //~ ++-+--+
    //~ ++--++-
    //~ ++--+-+
    //~ ++---++
    //~ +-+++--
    //~ +-++-+-
    //~ +-++--+
    //~ +-+-++-
    //~ +-+--++
    //~ +--+++-
    //~ +--++-+
    //~ +--+-++
    //~ +---+++
    //~ +------
    //~ -++++--
    //~ -+++-+-
    //~ -+++--+
    //~ -++-++-
    //~ -++-+-+
    //~ -++--++
    //~ -+-+++-
    //~ -+-++-+
    //~ -+-+-++
    //~ -+--+++
    //~ -+-----
    //~ --++++-
    //~ --+++-+
    //~ --++-++
    //~ --+-+++
    //~ --+----
    //~ ---++++
    //~ ---+---
    //~ ----+--
    //~ -----+-
    //~ ------+
     
     
    //~ ------
    //~ 2
    //~ 0 1
    //~ ------
    //~ 3
    //~ 2 2
    //~ ------
    //~ 4
    //~ 1 4
    //~ 4 1
    //~ ------
    //~ 5
    //~ 0 1
    //~ 3 9
    //~ ------
    //~ 6
    //~ 2 15
    //~ 5 6
    //~ ------
    //~ 7
    //~ 1 7
    //~ 4 34
    //~ 7 1
    //~ ------
    //~ 8
    //~ 0 1
    //~ 3 56
    //~ 6 28
    //~ ------
    //~ 9
    //~ 2 36
    //~ 5 125
    //~ 8 9
    //~ ------
    //~ 10
    //~ 1 10
    //~ 4 210
    //~ 7 120
    //~ 10 1
    

    发现如下结论:

    1. + 的个数以3为step迭代,即,+ 的个数 (cnt_+equiv 2 - n pmod 3);

    2. (n) 为偶数时,任意一种 + 的个数符合条件的方案都是合法的;

    3. (n)为奇数时,+ 的个数符合条件的方案只有一种不合法,即 +-+-+-+

    因此容易想到一种贪心的做法:将 (a) 从大到小排序,大的选 +,小的选 - 并特判上述不合法的情况。

    也可以用dp求解,这样不需要排序: (dp[i][j][0/1]) 表示前 (i) 个数,+ 的个数 (cnt_+equiv j pmod 3),是否形如 +-+-+-

    关于结论1,可以使用类似归纳法的东西证明:

    1. (n = 1, 2, 3) 时成立;
    2. (cnt_n = (i - cnt_i) + (n - i - cnt_{n - i}) = n - cnt_i - cnt_{n - i} equiv 2n - 4 equiv 2 - n pmod 3) 可知结论成立。

    关于结论 任意一种 + 的个数符合条件的方案都是合法的 的证明,方法类似,具体见官方题解

    #include <bits/stdc++.h>
    #ifdef LOCAL
    #define dbg(args...) std::cerr << "33[32;1m" << #args << " -> ", err(args)
    #else
    #define dbg(...)
    #endif
    inline void err() { std::cerr << "33[0m
    "; }
    template<class T, class... U>
    inline void err(const T &x, const U &... a) { std::cerr << x << ' '; err(a...); }
    template <class T>
    inline void readInt(T &w) {
      char c, p = 0;
      while (!isdigit(c = getchar())) p = c == '-';
      for (w = c & 15; isdigit(c = getchar());) w = w * 10 + (c & 15);
      if (p) w = -w;
    }
    template <class T, class... U>
    inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }
    template <class T, class U>
    inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
    template <class T, class U>
    inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
    
    typedef long long LL;
    typedef std::pair<int, int> PII;
    
    constexpr int N(2e5 + 5);
    int n, a[N];
    LL dp[N][3][2];
    int main() {
      readInt(n);
      for (int i = 1; i <= n; i++) readInt(a[i]);
      if (n == 1) return std::cout << a[1], 0;
      memset(dp, 0xcf, sizeof dp);
      dp[0][0][1] = 0;
      for (int i = 1; i <= n; i++) {
        for (int j = 0; j < 3; j++) {
          smax(dp[i][j][0], dp[i - 1][(j + 2) % 3][0] + a[i]);
          smax(dp[i][j][0], dp[i - 1][j][0] - a[i]);
          smax(dp[i][j][i & 1], dp[i - 1][(j + 2) % 3][1] + a[i]);
          smax(dp[i][j][i & 1 ^1], dp[i - 1][j][1] - a[i]);
        }
      }
      std::cout << dp[n][((5 - n % 3) % 3)][0];
      return 0;
    }
    
  • 相关阅读:
    hibernate连接MySQL配置hibernate.cfg.xml
    行百里者半九十 —— 查找算法
    行百里者半九十 —— 链表(1)
    行百里者半九十 —— 查找算法(中等)
    行百里者半九十 —— 字符串
    设计模式 —— 总结
    并发编程 —— 使用条件变量构建线程安全队列
    行百里者半九十 ——栈与队列
    设计模式 —— 命令模式
    css选择器中:firstchild与:firstoftype的区别
  • 原文地址:https://www.cnblogs.com/HolyK/p/13971947.html
Copyright © 2011-2022 走看看