C# · 12月 20, 2021

**hdu 2198 How many elements you must throw out? C语言动态规划题**

hdu 2198 How many elements you must throw out? C语言动态规划题

原题链接http://acm.hdu.edu.cn/showproblem.PHP?pid=2198

Problem Description

You have a sequence of numbers from which you must create the longest subsequence satisfying the following condition: it can be ‘cut’ into two parts that share exactly one common element (the last element of the first part is the first element of the second part),and the first part is sorted in strictly ascending order while the second part is sorted in strictly descending order. For example,the sequence { 1,4,6,5,2,1 } can be ‘cut’ into { 1,6 } and { 6,1 }. The two parts share the 6(see the following graph),and the first sequence is sorted in ascending order while the second sequence is sorted in descending order.

You are given a sequence of numbers. Output the minimal number of elements you must throw out from the given sequence such that the remaining subsequence satisfies the condition described above.

Input

There are multiple test cases. At first line of each case,there’s an integer N (1<=N<=50) and N integers followed at second line representing the subsequence,each of which ranges from 1 to 1000000000.Input ends when N is 0.

Output

Output the result for each case in one line.

Sample Input

6

1 4 6 5 2 1

5

2 2 2 2 2

Sample Output

4

题目意思

上面所说的大概就是一段连续数列中你要找出一个数,将这个数列从这个数分成两部分,然后前一个数列末尾数据是这个数,后一个数列开头是这个数。然后要满足前一个数列是递增的,后一个数列是递减的,如果不存在则删去某几个数使得这两个数列存在。求最少要删去几个数。

读懂题目其实很简单,其实就是求在某个数将该数列分成两个数列后,前一段数列的最长递增子序列(非连续)和后一段数列的最长递减子序列(非连续)的长度之和的最大值。

比如1 4 6 5 2 1,当选定数据6时候,分成 1 4 6和6 5 2 1两组数据,这时候他们的最长子序列分别是1 4 6和6 5 2 1,这时候就输出6(原先数列长度)+1(有一个数据重复)-3(前一段递增子序列长度)-4(后一段递减子序列长度)=0。

又比如5 4 3 2 1 2 3 4 5。将它分成九种不同的两段数列。当数据为第一个5(或者最后一个5)时,分成两个子数列分别为5以及1

5 4 3 2 1 2 3 4 5。这时候前一个最长子序列是2(本身),长度为1,后一个最长子序列是5 4 3 2 1,长度为5。

输出结果就是9+1-1-5=4。

由于数据数量比较少(我自己很菜也不知道除了这个办法有什么办法),将数列中每一个数遍历,然后求如果当前这个数是分割数的时候,两个数列里递增(递减)最长子序列长度之和,然后与求得的相比较求出最大值。

代码如下

#include

#include

#include

#include

using namespace std;

double a[55],b[55],dp1[55],dp2[55];

int main()

{

int n;

while(scanf(“%d”,&n)!=EOF&&n)

{

int mx=0;

for(int i=0;i<n;i++)

scanf(“%lf”,&a[i]);

int pos;// 用于记录当前分割位置

for(int k=0;k<n;k++){

pos=k;

int f=0;

for(int i=n-1;i>=pos;i–)

{

b[f++]=a[i]; //将后半部分求递减数列部分逆置变为求递增数列便于理解

}

for(int i=0;i<=50;i++)

dp1[i]=1; //将dp数组内元素初始化为1,因为如果前面没有元素满足比它小(大)时,它本身算为一个长度

int MAX1=1; //记录前一段数列中最长递增子序列的长度

for(int i=0;i<=pos;i++)

{

for(int j=0;j<i;j++)

{

if(a[i]>a[j])

{

dp1[i]=max(dp1[i],dp1[j]+1);

//dp1[j]代表到j位置的数时最长的子序列长度,如果a[i]比a[j]长,那么此时dp[i]的最长子序列长度为dp[j]+1或者位置j之前更长的子序列

if(dp1[i]>MAX1)

MAX1=dp1[i];

}

}

}

for(int i=0;i<=50;i++)

dp2[i]=1;

int MAX2=1;

for(int i=0;i<f;i++)

{

for(int j=0;j<i;j++)

{

if(b[i]>b[j])

{

dp2[i]=max(dp2[i],dp2[j]+1); //同上

if(dp2[i]>MAX2)

MAX2=dp2[i];

}

}

}

if(MAX1+MAX2>mx)

mx=MAX1+MAX2; //记录下最长的两部分子序列长度和

}

printf(“%dn”,n+1-mx); //由于有重复部分故需+1

}

return 0;

}

by有点弱智的鲤鱼王