1. 程式人生 > >求單調遞增最長子序列 動態規劃法(DP)

求單調遞增最長子序列 動態規劃法(DP)

單調遞增最長子序列


基本思想

動態規劃法重要的是確定狀態狀態轉移方程

狀態是區域性環境下得到的區域性解,後項的答案由前面的更小的項決定,前面的更小的項又由更小更小的項決定,直到到達一個邊界,這稱之為狀態轉移.如給出一個數組a[11]

如給出一個數組a[11]:


狀態當前的最長遞增子序列,用F[]陣列記錄,則dp[0]=1;


a[1]>a[0],dp[1]=dp[0]+1=2;a[2]>a[3],dp[2]=dp[1]+1=3;直到dp[5]=dp[4]+1=6;

重點:那麼dp[6]應該從哪裡得來?a[6]=11,找到a[i]<a[6]有i={0,1}而dp[0]=1,dp[1]=2;則dp[6]=max{dp[0],dp[1]}+1=dp[1]+1=3;接下來的變化:


從上面的分析中找到規律,即dp[i]的值是前面所有小於a[i]的陣列的dp[i]的最大值+1;

*此步驟程式碼如下:

dp[0]=1;
		for(i=1;i<N;i++){
			int max=-1;
			for(w=0;w<i;w++)if(a[w]<a[i]&&dp[w]>max)max=dp[w];
			dp[i]=max+1;
		}


得到狀態轉移方程:


最後遍歷dp[]陣列,其最大值即為最長子序列長度;

完整程式碼:(fond函式為後面的思考答案,與題目無關)
#include<cstring>
#include<cstdio>
const int M=100000;
int a[M],dp[M],L[M];


void found(int max,int N){
	int q,w,i=N-1;
	for(q=max;q>=1;q--){
		for(w=i;w>=0;w--){
			if(dp[w]==q){
				printf("%d ",a[w]);
				break;
			}
			i=w-1;
		}
	}
}


int main(){
	int N;
	while(scanf("%d",&N)!=EOF){
		int i,w;
		for(i=0;i<N;i++)scanf("%d",&a[i]);
		dp[0]=1;
		for(i=1;i<N;i++){
			int max=-1;
			for(w=0;w<i;w++)if(a[w]<a[i]&&dp[w]>max)max=dp[w];
			dp[i]=max+1;
		}
		int max=-1;
		for(i=0;i<N;i++)if(dp[i]>max)max=dp[i];
		printf("%d\n",max);
		found(max,N);
	}
}


思考:

題目只是要求最長的長度,如果要你輸出最長的最序列呢?

給出程式碼:

void found(int max,int N){
	int q,w,i=N-1;
	for(q=max;q>=1;q--){
		for(w=i;w>=0;w--){
			if(dp[w]==q){
				printf("%d ",a[w]);
				break;
			}
			i=w-1;
		}
	}
}

//大一菜鳥一枚,如有錯誤還望指正