您的位置:首页 > 其它

合唱队问题的动态规划解法

2013-12-05 17:47 176 查看
#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:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: