2017-09-01 11:29:43
writer:pprp
看sprout台湾大学acm教学视频的第一部分:
里边涉及到四道小例题
感觉很好就拿来写了写:
题意还有代码说明都在代码中:
1、最基础的骨牌问题:
/* @param:dp 入门 @writer:pprp @declare:最经典最简单的dp @begin:9:00 @end:10:00 @date:2017/9/1 */ #include <bits/stdc++.h> using namespace std; //未优化的最基础的 int fun(int n) { if(n == 1) return 1; if(n == 2) return 2; return fun(n-1) + fun(n-2); } //自顶向下的记忆化 //top down 小心递回过深 int dp[101] = {1,1,2}; int fun2(int n) { if(dp[n] != 0) return dp[n]; return fun2(n-1) + fun(n-2); } //自底向上 //子问题一定要比母问题要先跑到,注意递回跑法 int dp2[101] = {0}; void build() { dp2[1] = 1; dp2[2] = 2; for(int i = 3 ; i < 101; i++) dp2[i] = dp2[i-1] + dp2[i-2]; } int fun3(int n) { return dp2[n]; } int main() { build(); int a; while(cin >> a) { cout << fun(a) << endl; cout << fun2(a) << endl; cout << fun3(a) << endl; } return 0; }
2、涂色问题:题意见代码头
/* @param:dp 入门 @writer:pprp @declare:最经典最简单的dp,给红绿蓝三种颜色,让你求出蓝绿不相邻的排列n个元素的情况 @begin:10:10 @end:10:28 @date:2017/9/1 */ #include <bits/stdc++.h> using namespace std; /* 状态确定:按照最后一位的颜色确定状态 f(n,0):最后一位是红色的数量 f(n,1):最后一位是绿色的数量 f(n,2):最后一位是蓝色的数量 状态转移: f(n,0) = f(n-1,0) + f(n-1,1) + f(n-1,2) f(n,1) = f(n-1,0) + f(n-1,1) f(n,2) = f(n-1,0) + f(n-1,2) 初始条件: f(1,0) = 1; f(1,1) = 1; f(1,2) = 1; 最终答案: f(n,0) + f(n,1) +f(n,2); */ // top down int dp1[101][3] = {0}; void init() { dp1[1][0] = dp1[1][1] = dp1[1][2] = 1; } int fun(int n,int m) { if(dp1[n][m] != 0) return dp1[n][m]; if(m == 0) dp1[n][0] = fun(n-1,0) + fun(n-1,1) + fun(n-1,2); if(m == 1) dp1[n][1] = fun(n-1,0) + fun(n-1,1); if(m == 2) dp1[n][2] = fun(n-1,0) + fun(n-1,2); return dp1[n][m]; } //bottom up int dp2[101][3] = {3}; int build() { dp2[1][0] = dp2[1][1] = dp2[1][2] = 1; for(int i = 2; i < 101 ; i++) { dp2[i][0] = dp2[i-1][0] + dp2[i-1][1] + dp2[i-1][2]; dp2[i][1] = dp2[i-1][0] + dp2[i-1][1]; dp2[i][2] = dp2[i-1][0] + dp2[i-1][2]; } } int fun2(int n) { return dp2[n][0] + dp2[n][1] + dp2[n][2]; } //run int main() { int a; cin >> a; init(); build(); cout << fun(a,0) + fun(a,1) + fun(a,2) << endl; cout << fun2(a) << endl; return 0; }
3、骨牌问题2,加了一个L型骨牌
/* @param:dp 入门 @writer:pprp @declare:最经典最简单的dp, 给你2*1 & 3*1 L型骨牌填满2*n的格子,有几种拍法 @begin:10:36 @end:11:00 @date:2017/9/1 */ #include <bits/stdc++.h> using namespace std; /* 状态分析: f(n) 代表的是放n*2个格子时候的方法数 状态转移: 如果最后那块放2*1的:f(n) = f(n-1) + f(n-2) 如果最后那块放3*1的:f(n) = f(n-3) + f(n-4) + ... + f(0),由于对称的关系,要乘2 综合起来: f(n) = f(n-1) + f(n-2) + 2 * (f(n-3) + f(n-4) +...+ f(0)) 边界状态:f(0) = 1; f(1) = 1; f(2) = 2; */ //top down int dp[101] = {0}; void init() { dp[0] = dp[1] = 1; dp[2] = 2; } int fun(int n) { init(); if(dp[n] != 0) return dp[n]; dp[n] = 2 * fun(n-1) + fun(n-3); return dp[n]; } //bottom up int dp2[101] = {}; void build() { dp2[0] = dp2[1] = 1; dp2[2] = 2; for(int i = 3 ; i < 101 ; i++) dp2[i] = 2 * dp2[i-1] + dp2[i-3]; } int fun2(int n) { return dp2[n]; } int main() { int a; srand((int)time(NULL)); a = rand()%100; build(); cout << fun(a) << endl; cout << fun2(a) << endl; return 0; }
4.找到不相邻的数的最大和
/* @param:dp 入门 @writer:pprp @declare:最经典最简单的dp,给你一个正整数阵列, 从里边取出不相邻的数,问你取出数字和最大为多少? @begin:1:05 @end:11:25 @error:应该是从1开始不是从0开始 @date:2017/9/1 */ #include <bits/stdc++.h> using namespace std; /* 状态分析: f(n)代表的是当前取了这个位置以后的最大数字和 状态转移: f(n) = max(f(n-2),f(n-3) + arr[n]; 边界状态:f(0) = 0, f(1) = arr[1], f[2] = max(arr[1],arr[2]) */ //top down int arr[101] = {}; int dp[110] = {}; void init() { dp[0] = 0; dp[1] = arr[1]; dp[2] = max(arr[1],arr[2]); dp[3] = max(arr[1]+arr[3],arr[2]); } int fun(int n) { init(); if(dp[n] != 0) return dp[n]; dp[n] = max(fun(n-2),fun(n-3)) + arr[n]; return dp[n]; } int dp2[110] = {}; //bottom up void build() { dp2[0] = 0; dp2[1] = arr[1]; dp2[2] = max(arr[1],arr[2]); for(int i = 3; i < 101 ; i++) { dp2[i] = max(dp2[i-2],dp2[i-3]) + arr[i]; } } int fun2(int n) { return dp2[n]; } int main() { freopen("in.txt","r",stdin); int n; cin >> n; for(int i = 1 ; i <= n ; i++) cin >> arr[i]; init(); build(); cout << max(fun(4),fun(5)) << endl; cout << max(fun2(4),fun2(5)) << endl; return 0; }
总结:
状态确定很重要,利用对称的关系分析可以简化,
动态规划是一个走一步算一步的算法过程,不要全局的去分析,否则越分析越乱,按照每一步的走向来确定状态转移方程
做的多了动态规划才能有灵感
时间: 2024-12-30 02:37:48