• 最长上升子序列及拓展


    目录

    最长上升子序列

    怪盗基德的滑翔翼

    登山

    合唱队形

    友好城市

    拦截导弹

    导弹防御系统 

    最长公共上升子序列 

    朴素版(容易TLE) 

    优化


    最长上升子序列

    【题目描述】

    一个数的序列bibi,当b1

    你的任务,就是对于给定的序列,求出最长上升子序列的长度。

    【输入】

    输入的第一行是序列的长度N(1≤N≤1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

    【输出】

    最长上升子序列的长度。

    【输入样例】

    7
    1 7 3 5 9 4 8

    【输出样例】

    4
    1. #include
    2. using namespace std;
    3. const int N=1010;
    4. int f[N],a[N],g[N];
    5. int main()
    6. {
    7. int n;
    8. cin>>n;
    9. for(int i=0;i
    10. cin>>a[i];
    11. for(int i=0;i
    12. {
    13. f[i]=1;
    14. for(int j=0;j
    15. if(a[j]
    16. {
    17. f[i]=max(f[i],f[j]+1);
    18. // if(f[i]
    19. // {
    20. // f[i]=f[j]+1;
    21. // g[i]=j;
    22. // }
    23. }
    24. }
    25. // int k = 1;
    26. // for (int i = 1; i <= n; i ++)
    27. // if (f[i] > f[k]) k = i;
    28. // cout << f[k] << endl;
    29. // for (int i = 0, len = f[k]; i < len; i ++)
    30. // {
    31. // cout << a[k] << ' ';
    32. // k = g[k];
    33. // }
    34. int res=0;
    35. for(int i=0;i
    36. res=max(res,f[i]);
    37. cout<
    38. return 0;
    39. }

    怪盗基德的滑翔翼

    【题目描述】

    怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。

    有一天,怪盗基德像往常一样偷走了一颗珍贵的钻石,不料却被柯南小朋友识破了伪装,而他的滑翔翼的动力装置也被柯南踢出的足球破坏了。不得已,怪盗基德只能操作受损的滑翔翼逃脱。

    假设城市中一共有N幢建筑排成一条线,每幢建筑的高度各不相同。初始时,怪盗基德可以在任何一幢建筑的顶端。他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?

    【输入】

    输入数据第一行是一个整数K(K<100) ,代表有KK组测试数据。

    每组测试数据包含两行:第一行是一个整数N(N<100)N(N<100),代表有N幢建筑。第二行包含NN个不同的整数,每一个对应一幢建筑的高度h(0

    【输出】

    对于每一组测试数据,输出一行,包含一个整数,代表怪盗基德最多可以经过的建筑数量。

    【输入样例】

    3
    8
    300 207 155 299 298 170 158 65
    8
    65 158 170 298 299 155 207 300
    10
    2 1 3 4 5 6 7 8 9 10

    【输出样例】

    6
    6
    9
    1. #include
    2. using namespace std;
    3. const int N=1010,INF=0x3f3f3f3f;
    4. int a[N],f[N],g[N];
    5. int main()
    6. {
    7. int t;
    8. cin>>t;
    9. while(t--)
    10. {
    11. int n;
    12. cin>>n;
    13. memset(f,0,sizeof f);
    14. memset(g,0,sizeof g);
    15. for(int i=1;i<=n;i++)
    16. cin>>a[i];
    17. for(int i=1;i<=n;i++)
    18. {
    19. f[i]=1;
    20. for(int j=1;j<=i;j++)
    21. if(a[j]
    22. f[i]=max(f[i],f[j]+1);
    23. }
    24. for(int i=n;i>=1;i--)
    25. {
    26. g[i]=1;
    27. for(int j=n;j>=i;j--)
    28. if(a[j]
    29. g[i]=max(g[i],g[j]+1);
    30. }
    31. for(int i=1;i<=n;i++)
    32. f[i]=max(f[i],g[i]);
    33. int res=-INF;
    34. for(int i=1;i<=n;i++)
    35. res=max(res,f[i]);
    36. cout<
    37. }
    38. }

    登山

    【题目描述】

    五一到了,ACM队组织大家去登山观光,队员们发现山上一共有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?

    【输入】

    第一行:N (2 <= N <= 1000) 景点数;

    第二行:N个整数,每个景点的海拔。

    【输出】

    最多能浏览的景点数。

    【输入样例】

    8
    186 186 150 200 160 130 197 220

    【输出样例】

    4

    1. #include
    2. using namespace std;
    3. const int N=1010,INF=0x3f3f3f3f;
    4. int a[N],f[N],g[N];
    5. int main()
    6. {
    7. int n;
    8. cin>>n;
    9. for(int i=1;i<=n;i++)
    10. cin>>a[i];
    11. for(int i=1;i<=n;i++)
    12. {
    13. f[i]=1;
    14. for(int j=1;j<=i;j++)
    15. if(a[j]
    16. f[i]=max(f[i],f[j]+1);
    17. }
    18. for(int i=n;i>=1;i--)
    19. {
    20. g[i]=1;
    21. for(int j=n;j>=i;j--)
    22. if(a[j]
    23. g[i]=max(g[i],g[j]+1);
    24. }
    25. int res=-INF;
    26. for(int i=1;i<=n;i++)
    27. res=max(res,f[i]+g[i]-1);
    28. cout<
    29. }

    合唱队形

    【题目描述】

    NN位同学站成一排,音乐老师要请其中的(N−K)位同学出列,使得剩下的KK位同学排成合唱队形。

    合唱队形是指这样的一种队形:设KK位同学从左到右依次编号为1,2,…,K,他们的身高分别为T1,T2,…,TK,则他们的身高满足T1Ti+1>…>TK(1≤i≤K)。

    你的任务是,已知所有NN位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

    【输入】

    输入的第一行是一个整数N(2≤N≤100)N,表示同学的总数。第二行有nn个整数,用空格分隔,第ii个整数Ti(130≤Ti≤230)是第i位同学的身高(厘米)。

    【输出】

    输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

    【输入样例】

    8
    186 186 150 200 160 130 197 220

    【输出样例】

    4

    和登山一样,需要改一下结果,要取出人 

    1. #include
    2. using namespace std;
    3. const int N=1010,INF=0x3f3f3f3f;
    4. int a[N],f[N],g[N];
    5. int main()
    6. {
    7. int n;
    8. cin>>n;
    9. for(int i=1;i<=n;i++)
    10. cin>>a[i];
    11. for(int i=1;i<=n;i++)
    12. {
    13. f[i]=1;
    14. for(int j=1;j<=i;j++)
    15. if(a[j]
    16. f[i]=max(f[i],f[j]+1);
    17. }
    18. for(int i=n;i>=1;i--)
    19. {
    20. g[i]=1;
    21. for(int j=n;j>=i;j--)
    22. if(a[j]
    23. g[i]=max(g[i],g[j]+1);
    24. }
    25. int res=-INF;
    26. for(int i=1;i<=n;i++)
    27. res=max(res,f[i]+g[i]-1);
    28. cout<
    29. }

    友好城市

    【题目描述】

    Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。

    每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航线不相交的情况下,被批准的申请尽量多。

    【输入】

    第1行,一个整数N(1≤N≤5000),表示城市数。

    第2行到第n+1行,每行两个整数,中间用1个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。(0<≤xi≤10000)

    【输出】

    仅一行,输出一个整数,表示政府所能批准的最多申请数。

    【输入样例】

    7
    22 4
    2 6
    10 3
    15 12
    9 8
    17 17
    4 2

    【输出样例】

    4

    1. #include
    2. using namespace std;
    3. #define x first
    4. #define y second
    5. const int N=5010;
    6. typedef pair<int,int> PII;
    7. int f[N];
    8. PII p[N];
    9. int main()
    10. {
    11. int n;
    12. cin>>n;
    13. for(int i=1;i<=n;i++)
    14. {
    15. cin>>p[i].x>>p[i].y;
    16. }
    17. sort(p+1,p+n+1);
    18. for(int i=1;i<=n;i++)
    19. {
    20. f[i]=1;
    21. for(int j=1;j
    22. {
    23. if(p[i].y>p[j].y)f[i]=max(f[i],f[j]+1);
    24. }
    25. }
    26. int res=0;
    27. for(int i=1;i<=n;i++)
    28. res=max(f[i],res);
    29. cout<
    30. }

    拦截导弹

    【题目描述】

    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

    输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

    【输入】

    输入导弹依次飞来的高度。

    【输出】

    第一行:最多能拦截的导弹数;

    第二行:要拦截所有导弹最少要配备的系统数。

    【输入样例】

    389 207 155 300 299 170 158 65

    【输出样例】

    6
    2
    1. #include
    2. using namespace std;
    3. const int N=1010;
    4. int f[N],a[N];
    5. bool vis[N];
    6. int main()
    7. {
    8. int n=0;
    9. int x;
    10. while(cin>>x)a[++n]=x;
    11. int res=0;
    12. //求最长不上升子序列
    13. for(int i=n;i>=1;i--)
    14. {
    15. f[i]=1;
    16. for(int j=n;j>i;j--)
    17. if(a[i]>=a[j])
    18. f[i]=max(f[i],f[j]+1);
    19. res=max(res,f[i]);
    20. }
    21. cout<
    22. //求数组最少能被几个最长下降子序列全部覆盖
    23. int cnt=0,h=0,ans=0; //cnt是统计已经拦截的导弹
    24. //h每次拦截后的高度
    25. //ans最少导弹拦截系统
    26. while(cnt
    27. {
    28. ans++;
    29. h=30000;
    30. for(int i=1;i<=n;i++)
    31. {
    32. if(h>=a[i]&&!vis[i])
    33. {
    34. h=a[i]; //更新高度
    35. vis[i]=1; //被访问过
    36. cnt++;
    37. }
    38. }
    39. }
    40. cout<
    41. }

    导弹防御系统 

    导弹防御系统【剪枝:迭代加深/全局最小值】

    为了对抗附近恶意国家的威胁,RR 国更新了他们的导弹防御系统。

    一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降。

    例如,一套系统先后拦截了高度为 3 和高度为 4 的两发导弹,那么接下来该系统就只能拦截高度大于 44 的导弹。

    给定即将袭来的一系列导弹的高度,请你求出至少需要多少套防御系统,就可以将它们全部击落。

    输入格式

    输入包含多组测试用例。

    对于每个测试用例,第一行包含整数 nn,表示来袭导弹数量。

    第二行包含 n 个不同的整数,表示每个导弹的高度。

    当输入测试用例 n=0时,表示输入终止,且该用例无需处理。

    输出格式

    对于每个测试用例,输出一个占据一行的整数,表示所需的防御系统数量。

    数据范围

    1≤n≤50

    输入样例:

    1. 5
    2. 3 5 2 4 1
    3. 0

    输出样例:

    2
    

    样例解释

    对于给出样例,最少需要两套防御系统。

    一套击落高度为 3,4 的导弹,另一套击落高度为 5,2,1 的导弹。

    1. #include
    2. using namespace std;
    3. // 这题是拦截拦截第二问的加强版
    4. // 拦截导弹第二问是只考虑下降序列的方案数,因此直接用贪心搜出最优解即可(证明在那一题的笔记里)
    5. // 这一题,确实要考虑下降和上升两种序列的方案数
    6. // 因此只能用dfs进行爆搜两种方案的搭配,但无论是上升还是下降方案,依然采用上一题的贪心思路
    7. const int N = 55;
    8. int n;
    9. int a[N];
    10. int up[N], down[N];
    11. int res;
    12. //三个参数分别是考虑前u个导弹
    13. //已经采用了上升系统个数sum_up
    14. //和下降系统个数sum_down
    15. void dfs(int u, int sum_up, int sum_down) {
    16. //如果已经超过了最优解答案,那么直接剪枝
    17. if (sum_up + sum_down >= res) return;
    18. //没有超过最优解答案,且把所有导弹都考虑到了
    19. //那他就是当前最优解了
    20. if (u == n) {
    21. res = sum_up + sum_down;
    22. return;
    23. }
    24. //情况一:考虑用上升拦截系统来拦截第u个导弹
    25. // 上升拦截系统的贪心思路是:
    26. // 如果当前已有的上升拦截系统的高度都大于第u个导弹高度,则重新开一套系统
    27. // 否则,则由当前低于第u个导弹最高拦截系统来负责拦截
    28. int k = 0;
    29. while (k < sum_up && up[k] >= a[u]) ++k;
    30. //找到了有这么个拦截系统
    31. int t = up[k]; //t用于dfs回溯的时候恢复现场
    32. up[k] = a[u];
    33. if (k >= sum_up) dfs(u + 1, sum_up + 1, sum_down);
    34. else dfs(u + 1, sum_up, sum_down);
    35. //恢复现场
    36. up[k] = t;
    37. //情况二:考虑用下降拦截系统来拦截第u个导弹
    38. // 下降拦截系统的贪心思路是:
    39. // 如果当前已有的下降拦截系统的高度都小于第u个导弹高度,则重新开一套系统
    40. // 否则,则由当前大于第u个导弹最低拦截系统来负责拦截
    41. k = 0;
    42. while (k < sum_down && down[k] <= a[u]) ++k;
    43. t = down[k]; //t用于dfs回溯的时候恢复现场
    44. down[k] = a[u];
    45. if (k >= sum_down) dfs(u + 1, sum_up, sum_down + 1);
    46. else dfs(u + 1, sum_up, sum_down);
    47. //恢复现场
    48. down[k] = t;
    49. }
    50. int main() {
    51. while (cin >> n, n) {
    52. for (int i = 0; i < n; ++i) cin >> a[i];
    53. //最差情况是n个导弹分别用n个系统拦截
    54. //因此可以设置res初始为n来设立哨兵
    55. res = n;
    56. dfs(0, 0, 0);
    57. cout << res << endl;
    58. }
    59. return 0;
    60. }

    最长公共上升子序列 

    熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。

    小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。

    小沐沐说,对于两个数列 A 和 B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。

    奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。

    不过,只要告诉奶牛它的长度就可以了。

    数列 A 和 B 的长度均不超过 3000。

    输入格式

    第一行包含一个整数 N,表示数列 A,B 的长度。

    第二行包含 N 个整数,表示数列 A。

    第三行包含 N 个整数,表示数列 B。

    输出格式

    输出一个整数,表示最长公共上升子序列的长度。

    数据范围

    1≤N≤3000,序列中的数字均不超过 231−1。

    输入样例:

    1. 4
    2. 2 2 1 3
    3. 2 1 2 3

    输出样例:

    2

     

    朴素版(容易TLE) 

    1. #include
    2. using namespace std;
    3. const int N=3010;
    4. int a[N],b[N],f[N][N];
    5. int main()
    6. {
    7. int n,ans=0;
    8. scanf("%d",&n);
    9. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    10. for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    11. for(int i=1;i<=n;i++)
    12. for(int j=1;j<=n;j++)
    13. {
    14. f[i][j]=f[i-1][j];//所有不包含a[i]的最长公共上升子序列
    15. if(a[i]==b[j])//所有包含a[i]的最长公共上升子序列
    16. {
    17. f[i][j]=max(f[i][j],1);//当空的时候是自己,也就是1
    18. for(int k=1;k//求最长公共上升子序列
    19. if(b[k]
    20. f[i][j]=max(f[i][j],f[i][k]+1);
    21. }
    22. }
    23. //求最大值,第一个序列的前n个字母,与b的匹配最大
    24. for(int i=1;i<=n;i++) ans=max(ans,f[n][i]);
    25. printf("%d\n",ans);
    26. return 0;
    27. }

    优化

    1. #include
    2. using namespace std;
    3. const int N=3010;
    4. int a[N],b[N],f[N][N];
    5. int main()
    6. {
    7. int n;
    8. scanf("%d",&n);
    9. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    10. for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    11. for(int i=1;i<=n;i++)
    12. {
    13. int maxv=1;//由于在k中重复求前j个最大值,所有用一个maxv记录前j个的最长上升子序列的最大值
    14. for(int j=1;j<=n;j++)
    15. {
    16. f[i][j]=f[i-1][j];//所有不包含a[i]的最长公共上升子序列
    17. if(a[i]==b[j]) f[i][j]=max(f[i][j],maxv);//所有包含a[i]的最长公共上升子序列,更新最大值
    18. if(b[j]max(maxf,f[i][j]+1);//因为a[i]==b[j]的时候更新最大值,所以可以把b[j]换成a[i],用来与新的b[j]比较
    19. }
    20. }
    21. //最后更新一遍最大值
    22. int ans=0;
    23. for(int i=1;i<=n;i++) ans=max(ans,f[n][i]);
    24. printf("%d\n",ans);
    25. return 0;
    26. }

  • 相关阅读:
    第二章、数据结构和算法9分
    商业智能系统的主要功能包括数据仓库、数据ETL、数据统计输出、分析功能
    CentOS 离线升级Linux的内核并删除多有内核
    代码复现: VoxelNet论文和代码解析 pytorch版本(二) Dataloader.py
    医学图像 开源数据整理合集1
    忘记Grafana 密码怎么办 教你2种Grafana重置admin密码方法详细步骤
    【hcie-cloud】【1】华为云Stack解决方案介绍、华为文档获取方式 【上】
    (保姆级)服务器-Zabbix6.0使用Python脚本实现带图片的邮箱的报警
    136、LeetCode-496.下一个更大元素Ⅰ
    SQLite 数据库安装及使用(Linux)
  • 原文地址:https://blog.csdn.net/m0_64378422/article/details/127712575