1. 程式人生 > >**hdu 2198 How many elements you must throw out? C語言動態規劃題**

**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, 4, 6 } and { 6, 5, 2, 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
0

Sample Output
0
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以及
5 4 3 2 1 2 3 4 5。這時候前一個最長子序列是2(本身),長度為1,後一個最長子序列是5 4 3 2 1,長度為5。
輸出結果就是9+1-1-5=4。

由於資料數量比較少(我自己很菜也不知道除了這個辦法有什麼辦法),將數列中每一個數遍歷,然後求如果當前這個數是分割數的時候,兩個數列裡遞增(遞減)最長子序列長度之和,然後與求得的相比較求出最大值。

程式碼如下

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
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("%d\n",n+1-mx);  //由於有重複部分故需+1
	}
	return 0;
}

by有點弱智的鯉魚王