动态规划
我理解的动态规划就是,根据一定的初始状态,通过动态转移方程最终得到最优解的一种方法。所以最重要的就是明确动态转移方程以及初始状态。
poj上最经典的动态规划题目就是1088滑雪了,但是它还算是有点难度的。所以我按照做题的顺序,先讲一下最长有序子序列的问题。
1.2533 Longest Ordered Subsequence
解释: a1 < a2 < … < aN,即an是有序的,给出一个序列,希望求出这个序列的最长有序子序列。比如序列(1, 7, 3, 5, 9, 4, 8) 就有 (1, 7), (3, 4, 8)这样的子序列,但是(1,3,4,8)是最长的
解法:这道题算是非常入门的题目了。首先要有一个数组a保存所有的初始状态,其次要有一个数组来保存转移后的状态,我一般叫dp数组。
思路:dp数组是来保存最长子序列的长度的,dp[i]表示的是到i为止,最长子序列的长度是多少。
初始状态:设置dp全为0,设 dp[0]=1
状态转移:到第i处的最长子序列,若a[i]>a[j],那么dp[i]的值就是在dp[j]的基础上+1,最长子序列就是在小于a[i]的a[j]中,寻找dp值最大的dp[j]+1.也就是从a[0]到a[i-1],若a[i]>a[j]且dp[i]<dp[j]+1,则dp[i]=dp[j]+1
最优解:dp[0]到dp[n]中的最大数
简单的代码如下:
for(i=0;i<n;i++)
{
max=0;
for(j=0;j<i;j++)
if(a[j]<a[i])
if(max<dp[j])
max=dp[j];
dp[i]=max+1;
}
for(i=0;i<n;i++)
if(dp[i]>max)
max=dp[i];
printf("%d\n",max);
2.1088 滑雪
思路:这题可以延续上一题的思路,用一个二维数组dp[R][C]来表示可以滑的最长长度,稍微不同的是这个是二维的。
初始状态:dp全部置为0,如果四面都走不下就置为1,dp[i][j]=1
转移状态:a[i][j]对比周围的4个值,如果比a[i][j]小,就找出其中dp值最大的值max,dp[i][j]=max+1。注意边界的问题。
注意:有可能会出现周围几个dp值也不存在的情况,所以要用递推做。
完整代码如下:
#include<iostream>
#include<memory.h>
#include<stdio.h>
#define Max 105
using namespace std;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int k[Max][Max];
int r,c;
int dp[Max][Max];
int Dp(int i,int j)
{
int a,b;
int p;
int max=0;
if(dp[i][j]!=0)
return dp[i][j];
else
{
for(p=0;p<4;p++)
{
a=i+dir[p][0];
b=j+dir[p][1];
if(a>=0&&a<r&&b>=0&&b<c&&k[a][b]>k[i][j])
{
if(dp[a][b]==0)
dp[a][b]=Dp(a,b);
if(dp[a][b]>max)
max=dp[a][b];
}
}
return max+1;
}
}
int main()
{
int i,j,max,temp;
while(scanf("%d%d",&r,&c)!=EOF)
{
max=0;
memset(dp,0,sizeof(dp));
for(i=0;i<r;i++)
for(j=0;j<c;j++)
scanf("%d",&k[i][j]);
for(i=0;i<r;i++)
for(j=0;j<c;j++)
{
temp=Dp(i,j);
if(max<temp)
max=temp;
}
printf("%d\n",max);
}
return 0;
}
此外这两题还有时间复杂度更低的算法,大家可以去找找看.