动态规划__合唱队形问题

2024-01-28 23:32
文章标签 动态 规划 问题 合唱队

本文主要是介绍动态规划__合唱队形问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题描述
     N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。   合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,  则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。   你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。 
     输入格式 Input Format      输入的第一行是一个整数N(2<=N<=100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。 
     输出格式 Output Format      输出包括得到的最优队列的同学个数 以及最终同学的身高排列

思路
     这个问题可以分解为 对于0<=i<=n-1(n为同学数) 我们要求得满足子序列[0, i)的升序排列最优个数p加上子序列[i, n)满足降序最优个数q即p+q最大的i值 对于升序和降序的最优解 可以用动态规划来构造
     数组a[i]是第i个人的身高,b[i]是从左边第一个到a[i]的最长上升子序列,c[i]是从右边第一个到a[i]的最长上升子序列。这题的关键在于理解如何获得b[i]和c[i]的值。我们可以知道,不管a[i]为什么值时,b[i]>=1;(因为它本身就是一个上升序列元素。)假设 i=1 ,此时b[1]=1;因为a[1]的前面没有元素。那么如果a[2]>a[1],显然可以得到b[2]=b[1]+1; 因为a[1],a[2]是一个上升子序列。如果a[3]<=a[2]且a[3]>=a[1],那么b[2]为2;因为它可以和a[1]组成上升子序列。同理,若a[3]>a[1]..a[3-1],则b[3]=max{b[1]...b[3-1]}。由此我们可以得到递推关系式: b[i]=max{b[j](1<=j<i)|a[i]> a[j]}+1 (学过集合的应该看得懂。)同理可以求出c的值。 然后,可以得到符合合唱队的队列是b[i]+c[i]-1(a[i]被重复计算了一次),而题目要求的合唱队列是:max{b[i]+c[i]-1} 1<=i<=总人数,那么要挑出去多少人也就明白了,即 总人数 -  max{b[i]+c[i]-1
[cpp] view plain copy print ?
  1. //DJ.W 2012.11.9   
  2. //合唱队形问题:   
  3. //  问题描述:   给定一串整数 从中剔除一些数 使得其按从从小到大 再从大到小的顺序排列 如: 1 2 3 6 5 4 要求留下的数字尽可能多    
  4. //  输入:     数组大小 数组数据   
  5. //  输出:     能得到的最优队列元素个数 并输出元素队列   
  6. #include <iostream>   
  7. using namespace std;  
  8.   
  9. //用于保存子问题最优解的备忘录   
  10. typedef struct    
  11. {  
  12.     int maxlen; //当前子问题最优解   
  13.     int prev;   //构造该子问题所用到的下一级子问题序号(用于跟踪输出最优队列)   
  14. }Memo;  
  15.   
  16. //用于递归输出Memo B中的解   
  17. void Display(int* A, Memo* M, int i)  
  18. {  
  19.     if (M[i].prev == -1)  
  20.     {  
  21.         cout<<A[i]<<" ";  
  22.         return;  
  23.     }  
  24.     Display(A, M, M[i].prev);  
  25.     cout<<A[i]<<" ";  
  26. }  
  27.   
  28. //算法主要部分   
  29. void GetBestQuence(int* A, int n)  
  30. {  
  31.     //定义备忘录 并作必要的初始化   
  32.     Memo *B = new Memo[n];              //B[i]代表从A[0]到A[i]满足升序剔除部分元素后能得到的最多元素个数   
  33.     Memo *C = new Memo[n];              //C[i]代表从A[i]到A[n-1]满足降序剔除部分元素后能得到的最多元素个数   
  34.     B[0].maxlen = 1;        //由于B[i]由前向后构造 初始化最前面的子问题  (元素本身就是一个满足升序降序的序列)   
  35.     C[n-1].maxlen = 1;      //同样C[i]由后向前构造   
  36.     for (int i=0; i<n; i++) //为前一个最优子问题序号给定一个特殊标识-1    
  37.                             //用于在跟踪路径时终止递归或迭代(因为我们并不知道最终队列从哪里开始)   
  38.     {  
  39.         B[i].prev = -1;  
  40.         C[i].prev = -1;  
  41.     }  
  42.   
  43.     for (i=1; i<n; i++)      //构造B[n]   
  44.     {  
  45.         int max=1;  
  46.         for (int j=i-1; j>=0; j--)               //查看前面的子问题 找出满足条件的最优解 并且记录   
  47.         {  
  48.             if (A[j]<A[i] && B[j].maxlen+1>max)  
  49.             {  
  50.                 max = B[j].maxlen+1;            //跟踪当前最优解   
  51.                 B[i].prev = j;                  //跟踪构造路径   
  52.             }  
  53.         }  
  54.         B[i].maxlen = max;                      //构造最优解   
  55.     }  
  56.       
  57.     for (i=n-1; i>0; i--)  
  58.     {  
  59.         int max=1;  
  60.         for (int j=i; j<n; j++)          //从后往前构造 这是为了后面在统筹最终解时可以直接用B[i]+C[i]-1   
  61.                             //否则我们得到的最优解始终为B[n-1]+C[n-1]   
  62.         {  
  63.             if (A[j]<A[i] && C[j].maxlen+1>max)   //比当前长度更长 记录并构造   
  64.             {  
  65.                 max = C[j].maxlen+1;  
  66.                 C[i].prev = j;  
  67.             }  
  68.         }  
  69.         C[i].maxlen = max;  
  70.     }  
  71.   
  72.     //遍历i 得到最大的B[i]+C[i]-1(-1是因为我们在B[i]和C[i]中 均加上了A[i]这个数 因此需要减去重复的)   
  73.     int maxQuence = 0;  //记录当前最优解   
  74.     int MostTall;       //记录i 用于跟踪构造路径    
  75.     for (i=0; i<n; i++)  
  76.     {  
  77.         if (B[i].maxlen+C[i].maxlen-1 > maxQuence)  
  78.         {  
  79.             maxQuence = B[i].maxlen+C[i].maxlen-1;  
  80.             MostTall = i;  
  81.         }  
  82.     }  
  83.       
  84.     cout<<"最大合唱队形长度: "<<maxQuence<<endl;  
  85.       
  86.     //B由前向后构造 因此prev指向前面的元素 需要递归输出   
  87.     Display( A, B, MostTall);  
  88.     //C的prev指向后面元素 直接迭代输出   
  89.     while (C[MostTall].prev != -1)  
  90.     {  
  91.         MostTall = C[MostTall].prev;  
  92.         cout<<A[MostTall]<<" ";  
  93.     }  
  94.     cout<<endl;  
  95.       
  96.     delete []B;  
  97.     delete []C;  
  98. }  
  99. int main()  
  100. {  
  101.     //测试   
  102.     int *A;  
  103.     int n;  
  104.     cout<<"请输入合唱队员个数: "<<endl;  
  105.     cin>>n;  
  106.   
  107.     A = new int[n];  
  108.     cout<<"输入队员身高 :"<<endl;  
  109.     for (int i=0; i<n; i++)  
  110.     {  
  111.         cin>>A[i];  
  112.     }  
  113.     GetBestQuence(A, n);  
  114.     delete []A;  
  115.     return 0;  
  116. }  
//DJ.W 2012.11.9
//合唱队形问题:
//	问题描述:	给定一串整数 从中剔除一些数 使得其按从从小到大 再从大到小的顺序排列 如: 1 2 3 6 5 4 要求留下的数字尽可能多 
//	输入:		数组大小 数组数据
//	输出:		能得到的最优队列元素个数 并输出元素队列
#include <iostream>
using namespace std;//用于保存子问题最优解的备忘录
typedef struct  
{int maxlen;	//当前子问题最优解int prev;	//构造该子问题所用到的下一级子问题序号(用于跟踪输出最优队列)
}Memo;//用于递归输出Memo B中的解
void Display(int* A, Memo* M, int i)
{if (M[i].prev == -1){cout<<A[i]<<" ";return;}Display(A, M, M[i].prev);cout<<A[i]<<" ";
}//算法主要部分
void GetBestQuence(int* A, int n)
{//定义备忘录 并作必要的初始化Memo *B = new Memo[n];				//B[i]代表从A[0]到A[i]满足升序剔除部分元素后能得到的最多元素个数Memo *C = new Memo[n];				//C[i]代表从A[i]到A[n-1]满足降序剔除部分元素后能得到的最多元素个数B[0].maxlen = 1;		//由于B[i]由前向后构造 初始化最前面的子问题  (元素本身就是一个满足升序降序的序列)C[n-1].maxlen = 1;		//同样C[i]由后向前构造for (int i=0; i<n; i++) //为前一个最优子问题序号给定一个特殊标识-1 //用于在跟踪路径时终止递归或迭代(因为我们并不知道最终队列从哪里开始){B[i].prev = -1;C[i].prev = -1;}for (i=1; i<n; i++)		//构造B[n]{int max=1;for (int j=i-1; j>=0; j--)				//查看前面的子问题 找出满足条件的最优解 并且记录{if (A[j]<A[i] && B[j].maxlen+1>max){max = B[j].maxlen+1;			//跟踪当前最优解B[i].prev = j;					//跟踪构造路径}}B[i].maxlen = max;						//构造最优解}for (i=n-1; i>0; i--){int max=1;for (int j=i; j<n; j++)			//从后往前构造 这是为了后面在统筹最终解时可以直接用B[i]+C[i]-1//否则我们得到的最优解始终为B[n-1]+C[n-1]{if (A[j]<A[i] && C[j].maxlen+1>max)	//比当前长度更长 记录并构造{max = C[j].maxlen+1;C[i].prev = j;}}C[i].maxlen = max;}//遍历i 得到最大的B[i]+C[i]-1(-1是因为我们在B[i]和C[i]中 均加上了A[i]这个数 因此需要减去重复的)int maxQuence = 0;	//记录当前最优解int MostTall;		//记录i 用于跟踪构造路径 for (i=0; i<n; i++){if (B[i].maxlen+C[i].maxlen-1 > maxQuence){maxQuence = B[i].maxlen+C[i].maxlen-1;MostTall = i;}}cout<<"最大合唱队形长度: "<<maxQuence<<endl;//B由前向后构造 因此prev指向前面的元素 需要递归输出Display( A, B, MostTall);//C的prev指向后面元素 直接迭代输出while (C[MostTall].prev != -1){MostTall = C[MostTall].prev;cout<<A[MostTall]<<" ";}cout<<endl;delete []B;delete []C;
}
int main()
{//测试int *A;int n;cout<<"请输入合唱队员个数: "<<endl;cin>>n;A = new int[n];cout<<"输入队员身高 :"<<endl;for (int i=0; i<n; i++){cin>>A[i];}GetBestQuence(A, n);delete []A;return 0;
}

这篇关于动态规划__合唱队形问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/655136

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d