動態規劃學習筆記1

本文記錄自己的學習心得和一些要點,牆裂推薦參考此連接進行學習

一、從斐波那契入手

記憶搜索算法實現斐波那契 

int helper(vector<int>& memo, int n){
	if (n == 1 || n == 2)return 1;
	if (memo[n] != 0) return memo[n];//只要不是0就會記錄下來
	memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
	return memo[n];
}
int fib1(int n){
	if (n < 1) return 0;//n = 0的情況
	vector<int> meno(n + 1, 0);
	int res = helper(meno, n);
	return res;
}

自底向上實現斐波那契數列

int fib3(int n){
	vector<int>dp(n+1,0);
	dp[1] = dp[2] = 1;
	for (int i = 3; i <= n; ++i){
		dp[i] = dp[i - 1] + dp[i - 2];
	}
	return dp[n];
}

優化一下空間,之和兩個狀態相關,可以使用兩個變量替代

int fib4(int n){
	if (n == 1 || n == 2) return 1;
	int sum = 0;
	int pre = 1;//初始狀態
	int cur = 1;//初始狀態
	for (int i = 3; i <= n; ++i){
		sum = pre + cur;
		cur = sum;//狀態轉移
		pre = cur;//狀態轉移
	}
	return cur;
}

 二、湊零錢問題

主要找到狀態轉移方程和初始條件就可以了,狀態轉移方程一般時按照題目思路寫幾個,寫着寫着歸納法就出來了,初始條件也一樣。寫完之後就可以按照自底向上的方法法寫出代碼了。

 有了狀態轉移方程和初始條件就可以寫了.

int combinechange(vector<int>&coins,int n){
	if (n < 0) return -1;
	vector<int> dp(n + 1, 0);
	dp[0] = 0;
	for (int i = 1; i <= n; ++i){//計算所有值
		int res = INT_MAX;
		for (int j = 0; j < coins.size(); ++j){//遍歷coins,計算每一個值都需要所有的coins
			if (i>=coins[j]){//滿足i>coins[j],否則無解
				res = min(res, 1 + dp[i - coins[j]]);
			}
			else break;
		}
		dp[i] = res;
	}
	for (int i = 0; i < dp.size(); ++i){
		cout << dp[i] << endl;
	}
	return dp[n];
}

 修改一下dp數組初始條件得到更簡潔的代碼:

int combinechange1(vector<int>&coins, int n){
	if (n < 0) return -1;
	vector<int> dp(n + 1, n);//dp初始化爲n,最好選擇小的更新即可
	dp[0] = 0;
	for (int i = 1; i <= n; ++i){
		for (int j = 0; j < coins.size(); ++j){
			if (i >= coins[j]){
				dp[i] = min(dp[i], 1 + dp[i - coins[j]]);
			}
			else break;
		}
	}
	for (int i = 0; i < dp.size(); ++i){
		cout << dp[i] << endl;
	}
	return dp[n];
}

這裏的狀態方程中前一個狀態爲dp[i-coins[j]],故不像斐波那契那樣只和兩個狀態有關,這裏的dp數組就無法像斐波那契那樣直使用兩個變量去更新了。

三、最長遞增子序列問題(力扣300)

int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==0) return 0;
        vector<int> dp(nums.size(),1);
        dp[0]=1;
        for(int i = 1;i<nums.size();++i){
            for(int j = 0;j<i;++j){
                if(nums[i]>nums[j]){
                    dp[i] = max(dp[i],1+dp[j]);
                }
            }
        }
        int res = 0;
        for(int i = 0;i<dp.size();++i){
            res = max(res,dp[i]);
        }
        return res;
    }

 四、跳躍到數組最後一個元素需要最少的步數(力扣45)

趁熱打鐵,這個題和上一題套路都是一樣的

數組1:[2,3,1,1,4]  從2跳到3再跳到最後,需要兩步

數組2:[3,2,1,0,4]  無法跳到組後一個元素,返回-1

int jump(vector<int>& nums) {
        if(nums.size()==0) return 0;
        vector<int>dp(nums.size(),INT_MAX);
        dp[0]=0;
        for(int i = 1;i<nums.size();++i){
            for(int j = 0;j<i;++j){
                if(nums[j]>=(i-j)){
                    dp[i] = min(dp[i],1+dp[j]);
                }
            }
        }
        return dp[dp.size()-1];
    }

 這個題考的應該是貪心,用動態規劃做並不是最優的,對於全1也通不過。。。先不管了不影響我們理解動態規劃。

五、最大子序和

繼續趁熱打鐵做一下最大子序和,一樣的套路

 

 int maxSubArray(vector<int>& nums) {
        vector<int> dp(nums.size(),INT_MIN);
        dp[0] = nums[0];
        for(int i = 1;i<nums.size();++i){
            dp[i] = max(nums[i],nums[i]+dp[i-1]);
        }
        int res = dp[0];
        for(int i=0;i<dp.size();++i){
            res = max(res,dp[i]);
        }
        return res;
    }

 

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