算法题——动态规划
例题1:数字三角形
http://poj.org/problem?id=1163
首先使用递归的做法:
#include<iostream> using namespace std; #define MAX 101 int D[MAX][MAX]; //保存三角形 int n; int MaxSum(int i, int j) { if (i==n) { return D[i][j]; } int x = MaxSum(i+1, j); // 左下 int y = MaxSum(i+1, j+1); int tmp = x > y ? x : y; return tmp + D[i][j]; } int main() { int i, j; cin >> n; //输入三角形 for (i = 1; i <= n; i++) { for (j = 1; j <= i; j++) { cin >> D[i][j]; } } cout << MaxSum(1,1) << endl; return 0; }
因为存在太多的重复计算,当数据量大的时候,就会严重超时。
可以将每一次递归过程中,算出的值存起来,如果下次再进行计算时,如果之前已经计算过(已经被存起来了),那么就直接取存的值。
动态规划
状态方程:ans = max( maxSum(i+1, j),maxSum(i+1,j+1))+ a[i][j]
#include<iostream> using namespace std; #define MAX 101 int D[MAX][MAX]; //保存三角形 //用来保存每一个位置到底边路径的最大和(每次递归的结果),避免重复计算 int maxsum[MAX][MAX]; int n; //行数 int MaxSum(int i, int j) { // 如果之前已经计算过了,那么就直接使用结果,不用再次计算 if (maxsum[i][j] != -1) { return maxsum[i][j]; } if (i==n) { maxsum[i][j] = D[i][j]; } else { int x = MaxSum(i+1, j); // 左下 int y = MaxSum(i+1, j+1); int tmp = x > y ? x : y; maxsum[i][j] = tmp + D[i][j]; } return maxsum[i][j]; } int main() { int i, j; cin >> n; //输入三角形 for (i = 1; i <= n; i++) { for (j = 1; j <= i; j++) { cin >> D[i][j]; maxsum[i][j] = -1; // -1表示这个位置没有计算过 } } cout << MaxSum(1,1) << endl; return 0; }
最长上升最序列
http://bailian.openjudge.cn/practice/2757
#include<iostream> #include<algorithm> using namespace std; #define MAXN 1010 int a[MAXN]; //保存输入的数据 int maxLen[MAXN]; //用来保存每一个位置作为终点的最长子序列长度 int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { cin>> a[i]; maxLen[i] = 1; //每个位置作为终点的最长子序列长度初始为1,即自身 } //求第i个数作为终点的最长子序列长度 for (int i = 2; i <= n; i++) { //查看第j个数作为终点的最长子序列长度 for (int j = 1; j < i; j++) { if (a[i] > a[j]) { maxLen[i] = max(maxLen[i], maxLen[j] + 1); } } } int res = maxLen[1]; //依次循环遍历出每个元素作为终点的最长子序列,最大值即为整个数组的最长子序列 for (int i = 2; i < n; i++) { if (res < maxLen[i]) { res = maxLen[i]; } } cout<< res <<endl; }
字符串中找出连续最长的数字串
读入一个字符串str,输出字符串str中的连续最长的数字串输入描述:
每个测试输入包含1个测试用例,一个字符串str,长度不超过255。输出描述:
在一行内输出str中里连续最长的数字串。输入例子1:
abcd12345ed125ss123456789输出例子1:
123456789
和上面一个球最长连续子串的方法类似,都是倒着来,用一个数组来记录每一个字符的最长子串,如果当前字符时数字,并且前面一个字符大1,那么,这个位置的计数就是前一个字符计数加1.
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int main() { char s[300]; int dp[300]; while(gets(s)){ memset(dp, 0, sizeof(dp)); int length = strlen(s); int max_cnt = 0; int max_index = 0; for (int i = 1; i < length; i++) { //首先判断是不是数字,如果不是数字,则不作处理 if (s[i] >= '0' && s[i] <= '9' && (s[i] - s[i-1]) == 1) { dp[i] = dp[i-1] + 1; if (dp[i] > max_cnt) { max_cnt = dp[i]; max_index = i; } } } for (int i = max_index - max_cnt; i <= max_index; i++){ cout << s[i]; } cout << endl; } return 0; }
例题3:最长公共子串
http://poj.org/problem?id=1458
例题4:求出栈序列的总数
http://www.rqnoj.cn/problem/53
输入n,表示1,2,3...n依次入栈,出栈顺序不定,问有多少种出栈序列?
解析:使用动态规划的思想,状态方程:
res = func(i-1, j) + func(i+1, j-1)
出栈序列有入栈顺序和出栈顺序决定,用i表示栈中的元素,j表示为入栈的元素。
#include<iostream> using namespace std; int dp(int i, int j) { if(j == 0) { //已经没有入栈元素了 return 1; } int ans = dp(i+1, j-1); if (i > 0) { //栈中还有元素时 ans += dp(i-1, j); } return ans; } int main() { int n; while(cin >> n) { cout << dp(0, n) << endl; } return 0; }
至于判断一个出栈系列是否合法,可以参考:https://www.cnblogs.com/-beyond/p/6113702.html
例题4:传球游戏
http://www.rqnoj.cn/problem/487
状态方程:x表示球在哪里,k表示剩余的传球次数
res = func(x+1, k-1) + func(x-1, k-1),判断当k为0时,x为原始点,则计数加1。
#include<iostream> #include<cstring> using namespace std; int n,m,target = 1; int dp[31][31]; int left(int x){ if (x == 1) { return n; } else { return x-1; } } int right(int x){ if (x == n) { return 1; } else { return x+1; } } int f(int x, int k) { if (k == 0) { if (x == target) { return 1; //最终传递到了原始点 } return 0; //最终,球没有在原始点上 } if (dp[x][k] != -1) { return dp[x][k]; } dp[x][k] = f(left(x), k - 1) + f(right(x), k - 1); return dp[x][k]; } int main() { while(cin >> n >> m) { memset(dp, -1, sizeof(dp)); cout << f(1, m) << endl; } return 0; }
注意,dp数组的默认值不要总是设为0,因为有些数据可能导致无限递归。
如需转载,请注明文章出处,谢谢!!!