合唱队问题的动态规划解法
2013-12-05 17:47
183 查看
#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 就存储了从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 =1; //数组f用来记录从元素1开始截止到下表i的最长递增子序列的长度 //这里f[1]必须初始化为 for(int i=n-1;i>0;i--)//自底向上增加问题的规模,则f 就存储了从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||f1 ==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:
相关文章推荐
- TSP问题的动态规划解法(c#实现)
- 阿里云笔试题:最大子段和问题的动态规划解法
- 矩阵连乘问题的动态规划解法
- 0-1背包问题的动态规划解法为什么是NPC问题?
- 01背包问题的动态规划解法
- 0-1背包问题的动态规划解法
- 阿里云笔试题:最大子段和问题的动态规划解法
- 0-1背包问题的动态规划解法
- Predict the Winner一个动态规划的问题解法详解
- 0-1背包问题的动态规划解法
- TSP问题之动态规划解法
- 最长公共子序列问题的动态规划解法
- 0-1背包问题动态规划解法
- 最大子段和问题的动态规划解法
- 格子取数问题的动态规划和递归解法之比较
- 促销中“满X优惠”问题的两种解法:动态规划和枚举法
- robber问题的动态规划解法
- 为什么0-1背包问题是NP完全问题,但该问题还有动态规划的解法?
- 整数拆分问题的动态规划解法
- 动态规划总结——经典问题总结