动态规划(Dynamic Programming)学习 (一)

思想:

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。-《百度百科》

阶段 :将所给问题的过程,恰当的分为若干相互联系的阶段,以便能按一定的次序求解问题。阶段的划分一般是根据时间和空间的特征进行的,但是要能够把问题的过程转化为多阶段决策问题。
状态:状态表示每个阶段开始所处的自然状况或者客观条件。

题目:

两道poj的经典动态规划题:

POJ-1163 The Triangle

The Triangle
Time Limit: 1000MSMemory Limit: 10000k
Description
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

(Figure 1)Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.

InputYour program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but <= 100. The numbers in the triangle, all integers, are between 0 and 99. OutputYour program is to write to standard output. The highest sum is written as an integer.
Sample Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30


大概意思就是输入数字构成的三角形,求一条到最下面一层的路径(只能向下走或者向右下走),使经过的数字之和最大
只能往下或者往斜下走,于是可以分析出两种情况:
1.到达了底层 2.没有到达底层
最先想到的应该是递归的方法,如果到达了底层,就返回底层对应数字的值,否则返回下面和右下较大的值加上目前所处位置的值,更新状态(设三角形为D):
MaxDistance(I,j)=max(MaxDistance(i+1,j+1),MaxDistance(i+1,j))+D[i][j]
最终目的就是求MaxDistance[1][1],用c++来写:

int MaxDis1(int row,int col)//solution1:Recursion
{
 if(row==n)
 {
  return D[row][col];
 }
 else
 {
  return max(MaxDis1(row+1,col),MaxDis1(row+1,col+1))+D[row][col];
 }
}

这是未经优化的头最铁的递归方法,每走到下面一层都会重新计算前面的和,很耗时。优化的方法是记录下走到每一个地方的最大值(开一个二维数组),往下走的时候就可以直接使用了,上代码:

void reset()
{
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=i;j++)
  {
   Max[i][j]=-1;
  }
 }
}

int MaxDis2(int row,int col)//solution2:Recursion with memory
{
 if(Max[row][col]!=-1)
 {
  return Max[row][col];
 }
 if(row==n)
 {
  Max[row][col]=D[row][col];
 }
 else
 {
  Max[row][col]=max(MaxDis2(row+1,col),MaxDis2(row+1,col+1))+D[row][col];
 }
 return Max[row][col];
}

这种方法提交已经可以AC了,接下来尝试不用递归的方法,选择从最下面一层开始回推,更新Max[I][J]。一个二重循环就能搞定(先初始化最下面一层的值,赋给Max对应位置,循环从第n-1层开始)

void MaxDis3()//solution3
{
 for(int i=1;i<=n;i++)
 {
  Max[n][i]=D[n][i];
 }
 int i,j;
 for(i=n-1;i>=1;i--)
 {
  for(j=1;j<=i;j++)
  {
   Max[i][j]=max(Max[i+1][j+1],Max[i+1][j])+D[i][j];
  }
 }
 std::cout<<Max[1][1]<<std::endl;
}

还可以简化,数组开一维的就行了,每往上一层更新最大值时直接覆盖掉上一层对应位置的值。把包含了几种方法的程序贴上来:

#include<iostream>
#include<algorithm>
#include<string.h>
//POJ 1163 Recursive solution
using std::max;
const int MAXN=101;
int D[MAXN][MAXN];
int *Sum;
int Max[MAXN][MAXN];
int n;
//input triangle
void reset()
{
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=i;j++)
  {
   Max[i][j]=-1;
  }
 }
}
//
void InitTriangle()
{
 memset(D,0,sizeof(D));
 //Initialize
 std::cin>>n;
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=i;j++)
  {
   std::cin>>D[i][j];
  }
   }
}
//
int MaxDis1(int row,int col)//solution1:Recursion
{
 if(row==n)
 {
  return D[row][col];
 }
 else
 {
  return max(MaxDis1(row+1,col),MaxDis1(row+1,col+1))+D[row][col];
 }
}
//
int MaxDis2(int row,int col)//solution2:Recursion with memory
{
 if(Max[row][col]!=-1)
 {
  return Max[row][col];
 }
 if(row==n)
 {
  Max[row][col]=D[row][col];
 }
 else
 {
  Max[row][col]=max(MaxDis2(row+1,col),MaxDis2(row+1,col+1))+D[row][col];
 }
 return Max[row][col];
}
//
void MaxDis3()//solution3
{
 int i,j;
 for(i=n-1;i>=1;i--)
 {
  for(j=1;j<=i;j++)
  {
   Max[i][j]=max(Max[i+1][j+1],Max[i+1][j])+D[i][j];
  }
 }
 std::cout<<Max[1][1]<<std::endl;
}
void MaxDis4()//
{
 int i,j;
 Sum = D[n];
 for(i=n-1;i>=1;i--)
 {
  for(j=1;j<=i;j++)
  {
   Sum[j]=max(Sum[j],Sum[j+1])+D[i][j];
  }
 }
 std::cout<<Sum[1]<<std::endl;
}
int main()
{
 InitTriangle();
 std::cout<<MaxDis1(1,1)<<std::endl;
 //s1
 //
 reset();
 std::cout<<MaxDis2(1,1)<<std::endl;
 //s2
 //
 reset();
 for(int i=1;i<=n;i++)
 {
  Max[n][i]=D[n][i];
 }
 MaxDis3();
 //s3
 //
 MaxDis4();
 return 0;
}

结果:
在这里插入图片描述


POJ-1458 Common Subsequence

Time Limit: 1000MSMemory Limit: 10000K
Description:
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input:
abcfbc abfcab
programming contest
abcd mnp

Sample Output:
4
2
0

找两个字符串的最长公共子序列的长度。公共子序列是指序列中的每个字符都存在于那两个字符串,且排列顺序是一样的,有点求字符串的相似度的意思。
这道题也是动态规划的思想,开一个二维数组dp[][],大小为str1.length * str2.length,横向代表str1的每一个字符的序号,纵向代表str2的每一个字符的序号,dp[i][j]就是str1从第1到第I个字符,str2从第1到第j个字符,目前为止的最大公共子序列长度。若str1[i]==str2[j],则该位置的字符肯定在最终的公共子序列中,长度+1;若不等则str1[i],str2[j]至少有一个不在公共子序列中,长度取dp[i-1][j],dp[i][j-1]的较大值。于是目标就是求dp[str1.length][str2.length]
写出伪核心代码:
if str1[i]==str2[j] then
dp[i][j]=dp[i-1][j-1]+1
else
dp[i][j]=max(dp[I-1][j],dp[I][j-1])
思路比较清晰了,开始码代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
using std::max;
using std::string;
const int MAXN=1001;
//description:this is a DP solution for POJ-1458
int DP[MAXN][MAXN];
string str1="";
string str2="";
int main()
{
 while(std::cin>>str1>>str2)
 {
  int i,j;
  int len1=str1.length();
  int len2=str2.length();
  for(i=0;i<=len1;i++)
  {
   DP[i][0]=0;
  }
  for(j=0;j<=len2;j++)
  {
   DP[0][j]=0;
  }
  for(i=1;i<=len1;i++)
  {
   for(j=1;j<=len2;j++)
   {
    if(str1[i-1]==str2[j-1])
    {
     DP[i][j]=DP[i-1][j-1]+1;
    }
    else
    {
     DP[i][j]=max(DP[i-1][j],DP[i][j-1]);
    }
   }
  }
  std::cout<<DP[len1][len2]<<std::endl;
 }
 return 0;
}

结果:
在这里插入图片描述

分析出状态,列出状态方程是解决这类问题核心步骤

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值