poj入门水题--动态规划(DP)2533 Longest Ordered Subsequence 1088滑雪

动态规划

我理解的动态规划就是,根据一定的初始状态,通过动态转移方程最终得到最优解的一种方法。所以最重要的就是明确动态转移方程以及初始状态。

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;
}

此外这两题还有时间复杂度更低的算法,大家可以去找找看.





点赞