滑雪
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 93021 | Accepted: 35199 |
Description
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
Sample Output
25
解题历程:
这是第三次周赛的H题,当时第一反应是用BFS做,每个点查找一遍,但觉得可能会TLE,后来实践中也证实了这一点。
题目提示为DP,于我的理解中,DP无非就是两种方式:递归 or 递推
递归算法:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define N 101
using namespace std;
int map[N][N],len[N][N];
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int r,c;
int dp(int i,int j)
{
if(len[i][j]!=0)return len[i][j];
int maxx=0,s;
for(int t=0;t<4;t++)
{
int temx=i+dir[t][0],temy=j+dir[t][1];
if(temx>=0&&temx<r&&temy>=0&&temy<c&&map[temx][temy]<map[i][j])
{
s=dp(temx,temy);
if(s>maxx) maxx=s;//递归返还值和max的比较,及状态转移方程中的max{}部分
}
}
len[i][j]=maxx+1;
return maxx+1;
}
int main()
{
while(~scanf("%d%d",&r,&c))
{
int mx=-1;
memset(len,0,sizeof(len));
for(int i=0;i<r;i++)
for(int j=0;j<c;j++)
scanf("%d",&map[i][j]);
for(int i=0;i<r;i++)
for(int j=0;j<c;j++)
{
len[i][j]=dp(i,j);
if(len[i][j]>mx) mx=len[i][j];
}
printf("%d\n",mx);
}
return 0;
}
递推算法:
以前做过的最长合法序列(hoj 10179):
有k个整数A[1],A[2]...A[k],你需要从前往后选出若干个数,使得每一个后面的数都要大于或等于前面的数.例如,对于系列1,4,2,5,2,3,选出1,2,2,3是合法的,但是选出4,2,3是不合法的.
这也是一道经典的递推DP,但区别在于:
最长合法序列 它的 状态转移方程:
dp(i,A[i])=max{dp(i+1,A[i+1]),dp(i+1,A[i])}+1;
它写成递推的形式是一维的:
而这道题,其状态转移方程:
dp(i,j,H[i][j])=
max{dp(i+1,j,H[i+1][j]),dp(i-1,j,H[i-1][j],dp(i,j+1,H[i][j+1],dp(i,j-1,H[i][j-1]} + 1;
如果直接按照图的结构递推,那么就将是二维的形式,这样的话我并不会写递推。所以,将所有点按照H降序排列,这样就转化为一维的递推式。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int r,c,dir[4][2]={0,1,0,-1,1,0,-1,0},step[101][101],H[101][101];
struct dot{
int x;int y;int h;
}map[10001];
bool judge(int rr,int cc){
if(rr>=0&&cc>=0&&rr<r&&cc<c)
return true;
else return false;
}
bool cmp(dot u,dot v){
return u.h>v.h;//对h降序排列
}
int main()
{
scanf("%d%d",&r,&c);
int height;
for(int i=0;i<r;i++)
for(int j=0;j<c;j++)
{
scanf("%d",&height);
map[i*c+j].x=i;
map[i*c+j].y=j;
map[i*c+j].h=H[i][j]=height;
step[i][j]=1;
}
int len=c*r,maxx=-1;
dot temp;
sort(map,map+len,cmp);
for(int i=0;i<len;i++)
{
int xx=map[i].x,yy=map[i].y,hh=map[i].h;
for(int k=0;k<4;k++){
temp.x=xx+dir[k][0];temp.y=yy+dir[k][1];
if(judge(temp.x,temp.y))
{
temp.h=H[temp.x][temp.y];
if(temp.h<hh&&step[xx][yy]+1>step[temp.x][temp.y])
step[temp.x][temp.y]=step[xx][yy]+1;
}
}
}
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(maxx<step[i][j])
maxx=step[i][j];
}
//printf("step[%d]=%d\n",i,step[i]);
}
printf("%d\n",maxx);
return 0;
}
总结:DP思维很重要,现在还是菜得抠脚,要努力提高自己的姿势水平。