C. Compression and Expansion

题面


一个合法的表单由横向

N

N

N 行数字链,纵向一层或多层数字链组成,第

k

k

k 层的数字链(可以想象为前面打了

k

k

k 个制表符 )由

k

k

k 个数字组成,如

k

=

3

:

1.1.1

,

1.1.2

,

1.2.1

,

k=3:1.1.1,1.1.2,1.2.1,\cdots

k=3:1.1.1,1.1.2,1.2.1,⋯,

k

=

1

:

1

,

2

,

3

,

k=1:1,2,3,\cdots

k=1:1,2,3,⋯。每个形如

a

1

.

a

2

.

a

3

.


.

a

k

.

x

a_1.a_2.a_3.\cdots.a_k.x

a1​.a2​.a3​.⋯.ak​.x 的数字链都会在数字链

a

1

.

a

2

.

a

3

.


.

a

k

a_1.a_2.a_3.\cdots.a_k

a1​.a2​.a3​.⋯.ak​ 的后面,以及数字链

a

1

.

a

2

.

a

3

.


.

(

a

k

+

1

)

a_1.a_2.a_3.\cdots.(a_k+1)

a1​.a2​.a3​.⋯.(ak​+1)(如果有的话) 的前面,并且相互之间

x

x

x 按照从小到大的顺序(它们不一定相邻)。上图中的最左边就是一个合法例子,右边的不合法。

现在告诉你每一行的数字链的最后一个数字,要还原出任意一个合法的表单。

1

N

1000

1\leq N\leq 1000

1≤N≤1000.

题解

Dynamic Programming

先分析一下最后一个数字给我们的信息:

  • 如果此行是 1(最后数字),那么层数一定是上一行+1 。
  • 如果此行非 1 ,设其为

    i

    i

    i,那么和之前的某个末尾为

    i

    1

    i-1

    i−1 的同层。

这种方向不好分析,我们换一换:

  • 如果此行的下一行是 1,那么此行层数一定为下一行-1 ,且设此行的(末尾)数字为

    i

    i

    i ,后面可能有某数字为

    i

    +

    1

    i+1

    i+1 的行与此行最近同层。

  • 如果此行的下一行非 1,设此行数字为

    i

    i

    i ,那么此行后面一定不能直接接层数+1的行了,且后面与此行最近同层的行数字一定不为

    i

    +

    1

    i+1

    i+1。

这样好像更复杂了,但是我们可以依此想出一个

D

P

\rm DP

DP 的方法:

  • 维护两个值

    d

    p

    [

    i

    ]

    ,

    n

    e

    x

    t

    [

    i

    ]

    dp[i],next[i]

    dp[i],next[i] ,依次表示 (

    d

    p

    [

    i

    ]

    dp[i]

    dp[i]:)

    i

    i

    i 个位置向后延伸的最远距离,满足

    [

    i

    ,

    i

    +

    d

    p

    [

    i

    ]

    1

    ]

    [i,i+dp[i]-1]

    [i,i+dp[i]−1] 之间的行层数都不小于

    i

    i

    i 的层数,以及 (

    n

    e

    x

    t

    [

    i

    ]

    next[i]

    next[i]:) 满足

    d

    p

    [

    i

    ]

    dp[i]

    dp[i] 最大的情况下,后面与

    i

    i

    i 最近同层的

    a

    i

    +

    1

    a_i+1

    ai​+1 的位置,方便输出方案(没有就为

    N

    +

    1

    N+1

    N+1)

  • 从后往前计算,如果此行

    i

    i

    i 的下一行数字是 1,就令 dp[i]=dp[i+1]+1 ,然后枚举后面的所有

    a

    a

    a 值为

    a

    i

    +

    1

    a_i+1

    ai​+1 的行

    j

    j

    j(实为枚举

    n

    e

    x

    t

    [

    i

    ]

    next[i]

    next[i] 的可能值),若 dp[j]+(j-i) > dp[i] ,说明更优,此时更新 dp[i]=dp[j]+(j-i) , next[i]=j

  • 如果此行

    i

    i

    i 的下一行数字非 1,为

    a

    i

    +

    1

    a_i+1

    ai​+1,那么只好接着它:dp[i]=dp[i+1]+1 , next[i]=i+1

  • 如果此行

    i

    i

    i 的下一行数字非 1 且非

    a

    i

    +

    1

    a_i+1

    ai​+1,那么没法向后延伸了,dp[i]=1 , next[i]=N+1

  • 既然我们直到了每一行的

    n

    e

    x

    t

    [

    i

    ]

    next[i]

    next[i] ,那么就好输出方案了。

这样虽然有点大材小用,但是复杂度还是最优的

O

(

n

2

)

O(n^2)

O(n2) 。

CODE

  1. #include<set>
  2. #include<map>
  3. #include<queue>
  4. #include<cmath>
  5. #include<vector>
  6. #include<cstdio>
  7. #include<cstring>
  8. #include<iostream>
  9. #include<algorithm>
  10. using namespace std;
  11. #define MAXN 1005
  12. #define ENDL putchar('\n')
  13. #define LL long long
  14. #define DB double
  15. #define lowbit(x) ((-x) & (x))
  16. #define SI set<int>::iterator
  17. LL read() {
  18. LL f = 1,x = 0;char s = getchar();
  19. while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
  20. while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
  21. return f * x;
  22. }
  23. int n,m,i,j,s,o,k;
  24. int a[MAXN];
  25. int dp[MAXN],nx[MAXN];
  26. vector<int> bu[MAXN];
  27. void print(int l,int r,string ss) {
  28. if(l > r) return ;
  29. int p = l;
  30. while(p > 0 && p <= r) {
  31. cout<<ss<<a[p]<<endl;
  32. char s0[15]; sprintf(s0,"%d.",a[p]);
  33. int nn = nx[p];
  34. string s2 = ss + s0;
  35. print(p+1,min(r,nn-1),s2);
  36. p = nn;
  37. }
  38. return ;
  39. }
  40. int main() {
  41. int T = read();
  42. while(T --) {
  43. n = read();
  44. for(int i = 1;i <= n;i ++) a[i] = read(),bu[i].clear();
  45. dp[n] = 1;nx[n] = n+1;
  46. bu[a[n]].push_back(n);
  47. for(int i = n-1;i > 0;i --) {
  48. dp[i] = 1;nx[i] = n+1;
  49. if(a[i+1] == 1) {
  50. int le = dp[i+1],nm = a[i]+1;
  51. dp[i] = le+1;
  52. for(int jj = 0;jj < (int)bu[nm].size();jj ++) {
  53. int j = bu[nm][jj];
  54. if(j <= i+le+1 && dp[j]+(j-i) > dp[i]) {
  55. dp[i] = dp[j]+(j-i); nx[i] = j;
  56. }
  57. }
  58. }
  59. else if(a[i+1] == a[i]+1) {
  60. dp[i] = dp[i+1]+1; nx[i] = i+1;
  61. }
  62. bu[a[i]].push_back(i);
  63. }
  64. print(1,n,"");
  65. }
  66. return 0;
  67. }

Greedy

要是先祭出贪心方法,估计没人会看动规了吧

我们维护一个栈,若上一行的层数为

k

k

k ,那么从栈顶到栈底依次是当前层数为

k

k

k 的最后一行、层数为

k

1

k-1

k−1 的最后一行、层数为

k

2

k-2

k−2 的最后一行……

从前往后加行,每加一行

i

i

i 就分类:

  • a

    i

    >

    1

    a_i>1

    ai​>1:在栈中依次弹出栈顶,直到找到数字等于

    a

    i

    1

    a_i-1

    ai​−1 的那行

    j

    j

    j,然后成为它的后继,令 next[j]=i ,然后用

    i

    i

    i 替换

    j

    j

    j。

  • a

    i

    =

    1

    a_i=1

    ai​=1:加入栈顶。

这样为什么是正确的呢?

a

i

=

1

a_i=1

ai​=1 的情况就不用说了吧,只能加入栈顶。

a

i

>

1

a_i>1

ai​>1 时,没法增加层数了,那么一定得令栈中的某个数字等于

a

i

1

a_i-1

ai​−1 的行替换为

a

i

a_i

ai​,并且让该数上面的都弹出栈。那么为什么不最小化弹出栈的元素个数呢?这样后面能匹配到的机会就更多,一定是最优的。

z

x

y

:

O

(

n

)

\rm zxy:这样不就~O(n)~了吗?

zxy:这样不就 O(n) 了吗?

:

O

(

n

2

)

\rm 对曰:输出是~O(n^2)~的,你没法降维。

对曰:输出是 O(n2) 的,你没法降维。

CODE

Perfect Solution by jiangly

  1. #include <bits/stdc++.h>
  2. using i64 = long long;
  3. using u64 = unsigned long long;
  4. using u32 = unsigned;
  5. int main() {
  6. std::ios::sync_with_stdio(false);
  7. std::cin.tie(nullptr);
  8. int t;
  9. std::cin >> t;
  10. while (t--) {
  11. int n;
  12. std::cin >> n;
  13. std::vector<int> a(n);
  14. for (int i = 0; i < n; i++) {
  15. std::cin >> a[i];
  16. }
  17. std::vector<std::vector<int>> ans(n);
  18. ans[0] = {1};
  19. std::vector<int> stk{0};
  20. for (int i = 1; i < n; i++) {
  21. if (a[i] == 1) {
  22. ans[i] = ans[stk.back()];
  23. ans[i].push_back(1);
  24. stk.push_back(i);
  25. } else {
  26. while (ans[stk.back()].back() != a[i] - 1) {
  27. stk.pop_back();
  28. }
  29. ans[i] = ans[stk.back()];
  30. ans[i].back()++;
  31. stk.back() = i;
  32. }
  33. }
  34. for (int i = 0; i < n; i++) {
  35. for (int j = 0; j < int(ans[i].size()); j++) {
  36. std::cout << ans[i][j] << ".\n"[j == int(ans[i].size()) - 1];
  37. }
  38. }
  39. }
  40. return 0;
  41. }

[CF1523C] Compression and Expansion (DP/贪心)的更多相关文章

  1. 【bzoj4027】[HEOI2015]兔子与樱花 树形dp+贪心

    题目描述 很久很久之前,森林里住着一群兔子.有一天,兔子们突然决定要去看樱花.兔子们所在森林里的樱花树很特殊.樱花树由n个树枝分叉点组成,编号从0到n-1,这n个分叉点由n-1个树枝连接,我们可以把它 ...

  2. BZOJ 2021 [Usaco2010 Jan]Cheese Towers:dp + 贪心

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2021 题意: John要建一个奶酪塔,高度最大为m. 他有n种奶酪.第i种高度为h[i]( ...

  3. 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)

    洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...

  4. 【BZOJ-1046】上升序列 DP + 贪心

    1046: [HAOI2007]上升序列 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3723  Solved: 1271[Submit][Stat ...

  5. Codeforces 675E Trains and Statistic(DP + 贪心 + 线段树)

    题目大概说有n(<=10W)个车站,每个车站i卖到车站i+1...a[i]的票,p[i][j]表示从车站i到车站j所需买的最少车票数,求所有的p[i][j](i<j)的和. 好难,不会写. ...

  6. 【HDU 2546】饭卡(DP+贪心)

    贪心:最贵的留到最后买.状态转移方程:dp[j]=dp[j+a[i]]|dp[j],dp[i]表示余下i元. 原来就不足5元,那就不能买啦. #include<cstdio> #inclu ...

  7. POJ 1065 Wooden Sticks / hdu 1257 最少拦截系统 DP 贪心

    参考链接:http://blog.csdn.net/xiaohuan1991/article/details/6956629 (HDU 1257 解题思路一样就不继续讲解) POJ 1065题意:给你 ...

  8. HDU1069:Monkey and Banana(DP+贪心)

    Problem Description A group of researchers are designing an experiment to test the IQ of a monkey. T ...

  9. 线段树+dp+贪心 Codeforces Round #353 (Div. 2) E

    http://codeforces.com/contest/675/problem/E 题目大意:有n个车站,每个车站只能买一张票,这张票能从i+1到a[i].定义p[i][j]为从i到j所需要买的最 ...

随机推荐

  1. 1. 时序练习(广告渠道vs销量预测)

    用散点图来看下sales销量与哪一维度更相关. 和目标销量的关系的话,那么这就是多元线性回归问题了. 上面把所有的200个数据集都用来训练了,现在把数据集拆分一下,分成训练集合测试集,再进行训练. 可 ...

  2. Chrome自带功能实现网页截图

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年6月28日. 很简单,按下Ctrl+Shift+P,打开命令行窗口,如下图所示. 输入命令. Capture full size sc ...

  3. ShardingSphere-proxy-5.0.0分布式雪花ID生成(三)

    一.目的 保证在分库分表中每条数据具有唯一性 二.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Founda ...

  4. linux-python安装pip

    wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate sudo python3 get-pip.py linux 建立软连接 ...

  5. colab简易使用

    解压文件(zip文件) !unzip -o /content/drive/MyDrive/test.zip -d /content/ 解压test.zip到指定目录, 其他解压缩命令: linux-常 ...

  6. 你真的了解git的分支管理跟其他概念吗?

    现在前端要学的只是太多了,你是不是有时会有这个想法,如果我有两个大脑.一个学Vue,一个学React,然后到最后把两个大脑学的知识再合并在一起,这样就能省时间了. 哈哈,这个好像不能实现.现实点吧!年 ...

  7. MySQL-1-概念

    数据库相关概念 DB:数据库(database):存储数据的"仓库".它保存了一系列有组织的数据 DBMS:数据库管理系统,又称为数据库软件(产品),用于管理DB中的数据 SQL: ...

  8. NHibernte 4.0.3版本中,使用Queryover().Where().OrderBy().Skip().Take()方法分页获取数据失败

    问题代码如下: var result=repository.QueryOver<modal>() .Where(p=>p.Code==Code) .OrderBy(p=>p.I ...

  9. Android multiple back stacks导航的几种实现

    Android multiple back stacks导航 谈谈android中多栈导航的几种实现. 什么是multiple stacks 当用户在app里切换页面时, 会需要向后回退到上一个页面, ...

  10. Redis基础与性能调优

    Redis是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库.缓存服务或消息服务使用. Redis支持多种数据结构,包括字符串.哈希表.链表.集合.有序集合.位图.Hyperloglogs等. ...