算法题——动态规划
例题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,因为有些数据可能导致无限递归。
如需转载,请注明文章出处,谢谢!!!
浙公网安备 33010602011771号