合唱队问题的动态规划解法
来源:互联网 发布:龚德柏 知乎 编辑:程序博客网 时间:2024/05/19 14:39
#include <stdlib.h>#include "oj.h"#include <iostream>using namespace std;int lASS(int *a,int n,bool(*pfun)(int,int),int* f){ f[1]=1; //数组f用来记录从元素1开始截止到下表i的最长递增子序列的长度 //这里f[1]必须初始化为 for(int i=2;i<=n;i++)//自底向上增加问题的规模,则f[n]就存储了从1到n的最长递增子序列的长度 { f[i]=1;// 先初始化一个比较小的值 //for(int j=i-1;j>0;j--)//遍历第i个元素前面的所有元素 for(int j=1;j<i;j++)//遍历第i个元素前面的所有元素 { if(f[j]+1>f[i])//如果到下标j的最长递增子序列长度+1比当前f[i]大 { if(pfun(a[j],a[i])) //如果a[j]<a[i] { f[i]=f[j]+1;//则截止到i的最长递增子序列可增一 } } }//for cout<<"f["<<i<<"]="<<f[i]<<endl; } int max=1; for(int i=1;i<=n;i++) if(max<f[i]) max=f[i]; return max;}//function int lDSS(int *a,int n,bool(*pfun)(int,int),int* f){ f[n]=1; //数组f用来记录从元素1开始截止到下表i的最长递增子序列的长度 //这里f[1]必须初始化为 for(int i=n-1;i>0;i--)//自底向上增加问题的规模,则f[n]就存储了从1到n的最长递增子序列的长度 { f[i]=1;// 先初始化一个比较小的值 //for(int j=i-1;j>0;j--)//遍历第i个元素前面的所有元素 for(int j=n;j>i;j--)//遍历第i个元素前面的所有元素 { if(f[j]+1>f[i])//如果到下标j的最长递增子序列长度+1比当前f[i]大 { if(pfun(a[j],a[i])) //如果a[j]<a[i] { f[i]=f[j]+1;//则截止到i的最长递增子序列可增一 } } }//for cout<<"f["<<i<<"]="<<f[i]<<endl; } int max=1; for(int i=1;i<=n;i++) if(max<f[i]) max=f[i]; return max;}//function//#include "lASS.h"/*功能:计算最少出列多少位同学,使得剩下的同学排成合唱队形 输入参数: int N : 最初队列的N位同学 (2<=N<=50) char* statureArray:N位同学的身高字符串(保证只含数字和空格) 身高Ti要求(130<=Ti<=230)输出参数: int* rst: 最少出列多少位同学,使得剩下的同学排成合唱队形返回值: 0: 成功; -1:异常 */bool parseArray(char* Sa,int* a,int n){ char* pstr=Sa; cout<<endl; for(int i=1;i<=n;i++) { a[i]=atoi(pstr); pstr+=4; cout<<"a["<<i<<"]"<<"is "<<a[i]<<endl; } return true; }int chorus(int N, char* StatureArray, int* Rst){ int* a=new int[N+1];; parseArray(StatureArray,a,N); int* f1=new int[N+1]; int* f2=new int[N+1]; cout<<"the longest ascend sub sequence"<<lASS(a,N,st,f1)<<endl; cout<<"the longest descend sub sequence"<<lDSS(a,N,st,f2)<<endl; if(f1[N]==N||f1[N]==N) return false;// 单调序列无法组成合唱队 int max=0; for(int i=1;i<=N;i++) if(max<f1[i]+f2[i]-1) max=f1[i]+f2[i]-1; *Rst=N-max; cout<<endl<<*Rst<<endl; return 0;}
问题描述:
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, …, K,他们的身高分别为T1, T2, …, TK,
则他们的身高满足T1 < T2 < … < Ti , Ti > Ti+1 > … > TK (1 <= i <= K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
功能描述:计算最少出列多少位同学,使得剩下的同学排成合唱队形
输入参数:
int N : 最初队列的N位同学 (2<=N<=50)
char* statureArray:N位同学的身高字符串(保证只含数字和空格)
身高Ti要求(130<=Ti<=230)
输出参数:
int* rst: 最少出列多少位同学,使得剩下的同学排成合唱队形
返回值:
0: 成功; -1:异常
分析:问题的关键在于同学的排列顺序不能改变,这就降低了问题的复杂度,可以用动态规划来求解。
这样分析就是典型的最长下降子序列问题。只要枚举每一个人站中间时可以的到的最优解。显然它就等于,包括他在内向左求最长上升子序列,向右求最长下降子序列。
其实最长子序列只要一次就可以了。因为最长下降(上升)子序列不受中间人的影响。
只要用OPT1求一次最长上升子序列,OPT2求一次最长下降子序列。这样答案就是N-max<i>(opt1[i]+opt2[i]-1).
...........
思路:该题的关键是不能置换队员的顺序,这个要求降低了问题的难度,如果可以排列的话就难了,建一个堆总会出现重复的元素,如何调整还没想出来怎么做。
考虑不能排列的情况,先对整个队列求一个最长递增子序列再求一个最长递降子序列。则剩下的人数就好算了。因为最长递增子序列是一个典型的动态规划问题,设子问题的解是
FAscend[i]和FDescend[i]则合唱队问题的最优解是N-max[i]{FAscend[i]+ FDescend[i]-1}
关键:最优子结构的性质,递归定义子问题的解,自底向上求解
一, 最长递增子序列问题的描述:设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。
二, 最长递增子序列问题的最优子结构性质:
设f(i)表示序列中以ai为末尾元素的最长递增子序列的长度。在求以ai为末尾的最长递增子序列时,找到所有序号在L前面且小于ai的元素aj,即j<I,且aj<ai。如果这样的元素存在,那么对于所有的aj,都有一个以aj为末尾元素的最长递增子序列的长度,把其中最大的f(j)选出来,那么f(i)就等于最大的f(j)+1
F(i)=max[j]{f(j)+1},即以ai为末尾元素的最长递增子序列,等于以使f(j)最大的哪个aj为末尾元素的递增子序列最末再加上ai;如果这样的元素不存在,那么ai自身构成一个长度为1的以ai为末尾元素的递增子序列。
三, 实现:用f【】来保存最优值,外层循环扩大问题规模,即数组f的下表递增,内层循环遍历从i到1的子问题的最优质。
implementation:
- 合唱队问题的动态规划解法
- 动态规划求解合唱队问题的思路
- 背包问题的动态规划解法
- 01背包问题的动态规划解法
- robber问题的动态规划解法
- 【oj平台】合唱队&动态规划
- 华为在线编程系列-合唱队(动态规划的应用)
- 0-1背包问题的动态规划解法
- 0-1背包问题的动态规划解法
- TSP问题的动态规划解法(c#实现)
- 0-1背包问题的动态规划解法
- 动态规划 钢条切割问题的朴素解法
- 同一问题的递归与动态规划解法
- 矩阵连乘问题的动态规划解法
- Predict the Winner一个动态规划的问题解法详解
- 最大子段和问题的动态规划解法
- 最长递增子序列的问题----两个经典题目:合唱队和Redraiment走法(简单动态规划)
- 石子合并问题 --动态规划--解法1
- [036] 微信公众帐号开发教程第12篇-符号表情的发送(下) .
- hbase 分页浏览实现
- Rails中的REST路由规则
- 语音中的语谱图
- Oracle高效设计-Tom kyte著
- 合唱队问题的动态规划解法
- 使用 python 遍历目录下的文件
- oracle pl/sql的进价、编写分页过程
- strspn vs strcspn
- [037] 微信公众帐号开发教程第13篇-图文消息全攻略 .
- 黑马程序员_反射技术和正则表达式
- SQLite编程操作
- strspn vs strcspn
- C运行时库CRT(C Run-time Library)详解