複試上機指南之貪心、動態規劃

貪心

貪心求解的是局部最優解,只有在無後效性的情況下,求解的纔可能是全局最優解。

看了簡單貪心和區間貪心,就上機考試來說,關於貪心的套路就是找到每一個部分的局部最優解,且無後效性,即當前的選擇不會影響後面部分的求解。大多數題目都是選擇好一個屬性,進行排序,然後設置一個標誌,循環的去操作這個已經排好序的屬性,直到不滿足題意爲止。

動態規劃

動態規劃求解的是最優解問題,與分治法的思想類似,將待求解問題分解成若干子問題,不同在於分治法可能會對一個子問題求解多次,但是動態規劃是每次把求解的子問題的答案保存下來,在需要的時候直接取即可。

最大子序列和

動態規劃經典問題之一,在一個給定的序列中,找一個連續的子序列,使得子序列的和爲最大。
首先設置一個dp[],令dp[i]表示以A[i]作爲末尾的連續序列的最大和,於是最大連續子序列和便是數組dp的最大值;這個題有兩種情況:1.只有一個元素即A[i]本身,dp[i]=A[i]; 2.有多個序列,從某個A[j]開始,直到A[i],也就是dp[i]=A[j]+…+A[i-1]+A[i],即dp[i] = dp[i-1]+A[i],最後求得max(A[i],dp[i-1]+A[i])即可。

#include<iostream>
using namespace std;
const int maxn = 1000;
long long dp[maxn]; 
long long arr[maxn];
long long MaxSubsequence(int n){
	long long maximum = 0;
	for(int i=0;i<n;i++){
		if(i==0){
			dp[i]=arr[i];
		}else{
			dp[i]=max(arr[i],dp[i-1]+arr[i]);
		}
		maximum = max(maximum,dp[i]);
	}
	return maximum;
}
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}
	long long answer = MaxSubsequence(n);
	cout<<answer<<endl;
	return 0;
}
最大子矩陣

給一個二維矩陣,求其中的最大子矩陣的和。
無非兩種情況:1. i==j時,就是一個一維矩陣,求一個序列的連續子序列的和;2. i!=j,將i到j行的元素加起來,得到只有一行的一維數組,這個一維數組的最大連續子序列和便是最大子矩陣和。

#include<iostream>
using namespace std;
const int maxn = 100;
int matrix[maxn][maxn];
int total[maxn][maxn];
int arr[maxn];
int dp[maxn];
int MaxSubsequence(int n){
	int maximum=0;
	for(int i=0;i<n;i++){
		if(i==0){
			dp[i] = arr[i];
		}else{
			dp[i] = max(arr[i],dp[i-1]+arr[i]);
		}
		maximum = max(maximum,dp[i]);
	}
	return maximum;
}
int MaxSubmatrix(int n){
	int maximal = 0;
	for(int i=0;i<n;i++){
		for(int j=i;j<n;j++){
			for(int k=0;k<n;k++){
				if(i==0){
					arr[k] = total[j][k]; 
				}else{
					arr[k] = total[j][k]-total[i-1][k];
				}
			}
			int current = MaxSubsequence(n);
			maximal = max(maximal,current);
		}
	}
	return maximal;
}

int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>matrix[i][j];
		}
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			if(i==0){
				total[i][j] = matrix[i][j];
			}else{
				total[i][j] = total[i-1][j] + matrix[i][j];
			}
		}
	}
	int answer = MaxSubmatrix(n);
	cout<<answer<<endl;
	return 0;
}

最長遞增子序列

即給定一個序列,取出若干元素(不必連續)組成一個新的序列,序列中的各個數依舊保持原序列中的先後順序。若對新序列中的任意下標x<y且Ax<Ay,成爲一個遞增子序列。最長遞增子序列即求遞增的子序列中的最長的那個。
設置一個dp[],令dp[i]表示以A[i]作爲末尾的最長遞增子序列的長度,於是最長遞增子序列的長度便是數組dp中的最大值;只有兩種情況:1. A[i]之前的二元素都比A[i]大,即最長遞增子序列只有A[i]本身,即dp[i]=1;2. A[i]之前的元素A[j]都比A[i]小,因此只需將A[i]添加到以A[j]作爲末尾的最長遞增子序列,構成一個新的子序列。dp[j]就是以A[j]作爲末尾的最長遞增子序列長度,這個新序列的長度爲dp[i] = dp[j]+1。
下面的代碼是求,最長不增子序列,最長遞增其實類似,只不過狀態方程不一樣了。

#include<iostream>
using namespace std;
const int maxn = 25;
int height[maxn];
int dp[maxn];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>height[i];
	}
	int answer = 0;
	for(int i=0;i<n;i++){
		dp[i] = 1;
		for(int j=0;j<i;j++){
			if(height[i]<=height[j]){
				dp[i] = max(dp[i],dp[j]+1);
			}
		}
		answer = max(answer,dp[i]);
	}
	cout<<answer<<endl;
	return 0;
}

求最大遞增子序列的和最大值,只需將A[i]添加到以A[j]作爲末尾的遞增子序列構成新的子序列,而這個新的子序列和爲dp[i] = dp[j]+A[i]。只需將i之前的元素逐一遍歷,以便獲得以A[i]作爲結尾的最大上升子序列和dp[i]。

#include<iostream>
using namespace std;
const int maxn = 1000;
int arr[maxn];
int dp[maxn];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}
	int answer = 0;
	for(int i=0;i<n;i++){
		dp[i] = arr[i];
		for(int j=0;j<i;j++){
			if(arr[i]>arr[j]){
				dp[i] = max(dp[i],dp[j]+arr[i]);
			}
		}
		answer = max(answer,dp[i]);
	}
	cout<<answer<<endl;
	return 0;
}

最長公共子序列

未完待續。。。。。

揹包問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章