Codeforces Gym 101623A Ascending Photo - 动态规划

题目传送门

  传送门

题目大意

  给定一个长度为$n$的序列,要求划分成最少的段数,然后将这些段排序使得新序列单调不减。

  考虑将相邻的相等的数缩成一个数。

  假设没有分成了$n$段,考虑最少能够减少多少划分。

  我们将这个序列排序,对于权值相同的一段数可以任意交换它们,每两个相邻数在原序列的位置中如果是$i, i + 1$,那么划分的段数就可以减少1.

  每次转移我们考虑添加值相同的一段。

  每次转移能不能将减少的段数加一取决于当前考虑的数在前一段内有没有出现以及有没有作为最左端点。

  因此我们记录一个决策与最优解不同的次优解就能转移了。

Code

  1 /**
  2  * Codeforces
  3  * Gym#101623A
  4  * Accepted
  5  * Time: 171ms
  6  * Memory: 18300k
  7  */
  8 #include <algorithm>
  9 #include <iostream>
 10 #include <cassert>
 11 #include <cstdlib>
 12 #include <cstdio>
 13 using namespace std;
 14 typedef bool boolean;
 15 
 16 #define pii pair<int, int>
 17 #define fi first
 18 #define sc second
 19 
 20 ostream& operator << (ostream& out, pii x) {
 21     out << "(" << x.fi << ", " << x.sc << ")";
 22     return out;
 23 }
 24 
 25 template <typename T>
 26 void pfill(T* pst, const T* ped, T val) {
 27     for ( ; pst != ped; *(pst++) = val);
 28 }
 29 
 30 template <typename T>
 31 void pcopy(T* pst, const T* ped, T *pval) {
 32     for ( ; pst != ped; *(pst++) = *(pval++));
 33 }
 34 
 35 int n;
 36 int *ar;
 37 pii *ps;
 38 
 39 inline void init() {
 40     scanf("%d", &n);
 41     ar = new int[(n + 1)];
 42     for (int i = 1; i <= n; i++)
 43         scanf("%d", ar + i);
 44 }
 45 
 46 int *ss, *st;
 47 boolean *exi;
 48 inline void solve() {
 49     ps = new pii[(n + 1)];
 50     ss = new int[(n + 1)];
 51     st = new int[(n + 1)];
 52     exi = new boolean[(n + 1)];
 53     pfill(exi, exi + n + 1, false);
 54     int m = 0, diff = 0;
 55     for (int i = 1; i <= n; i++)
 56         if (i == 1 || ar[i] != ar[i - 1])
 57             ps[++m] = pii(ar[i], ++diff);
 58     sort(ps + 1, ps + (n = m) + 1);
 59 //    for (int i = 1; i <= m; i++)
 60 //        cerr << ps[i] << endl;
 61     ss[1] = 1;
 62     for (int i = 2; i <= n; i++)
 63         ss[i] =    ((ps[i - 1].first == ps[i].first) ? (ss[i - 1]) : (i));
 64     st[n] = n;
 65     for (int i = n - 1; i; i--)
 66         st[i] = ((ps[i + 1].first == ps[i].first) ? (st[i + 1]) : (i));
 67 
 68     ss[0] = st[0] = 0;
 69     pii f(0, -1), g(-14285700, -1), cf(-1, -1), cg(-1, -1);
 70     for (int i = 1; i <= n; i++) {
 71         if (ss[i] != i)
 72             continue;
 73         for (int j = max(ss[i - 1], 1); j <= st[i - 1]; j++)
 74             exi[ps[j].second] = true;
 75         for (int j = ss[i], x, uval; j <= st[i]; j++) {
 76             x = ps[j].second;
 77             if (exi[x - 1]) {
 78                 if (x - 1 == f.second && st[i - 1] > ss[i - 1])
 79                     assert(x - 1 != g.second), uval = g.first + 1;
 80                 else
 81                     uval = f.first + 1;
 82                 uval = max(uval, f.first);
 83             } else
 84                 uval = f.first;
 85             if (uval > cf.first)
 86                 cg = cf, cf = pii(uval, x);
 87             else if (x != cg.second && uval > cg.first)
 88                 cg = pii(uval, x);
 89         }
 90         for (int j = max(ss[i - 1], 1); j <= st[i - 1]; j++)
 91             exi[ps[j].second] = false;
 92         swap(cf, f);
 93         swap(cg, g);
 94         cf = pii(-14285700, -1), cg = pii(-14285700, -1);
 95 //        cerr << f << " " << g << endl;
 96     }
 97     printf("%d\n", n - f.first - 1);
 98 }
 99 
100 int main() {
101     init();
102     solve();
103     return 0;
104 }

转载于:https://www.cnblogs.com/yyf0309/p/9846698.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值