您的位置:首页 > 其它

poj 1088/2111 滑雪(经典dp/字典序输出)

2015-07-21 20:37 239 查看
题意:一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。

思路:动态规划。正向递推和递归(记忆化搜索)方式都可以。用递推时注意需要将高度排序。需要注意一个细节,如果一个位置比它四周的位置都低,那么它的高度为1,而不是0.

递推:

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define INF 0x3fffffff
using namespace std;
#define N 105
int dp

,s

;
struct point{
int x,y,h;
}p[N*N];
int n,m,top = 0;
int ori[4][2] = {{-1,0},{1,0},{0,1},{0,-1}};
int cmp(struct point a,struct point b){
return a.h < b.h;
}
int main(){
int i,j,x,y,res=0;
scanf("%d %d",&n,&m);
memset(dp,0,sizeof(dp));
memset(s, 0, sizeof(s));
for(i = 1;i<=n;i++)
for(j = 1;j<=m;j++){
scanf("%d",&s[i][j]);
p[top].x = i;
p[top].y = j;
p[top++].h = s[i][j];
}
sort(p,p+top,cmp);
for(i = 0;i<top;i++){
x = p[i].x;
y = p[i].y;
dp[x][y] = 1;//仔细理解题意很重要,贡献WA
for(j = 0;j<4;j++)
if(p[i].h > s[x+ori[j][0]][y+ori[j][1]])
dp[x][y] = max(dp[x][y],dp[x+ori[j][0]][y+ori[j][1]]+1);
res = max(res,dp[x][y]);
}
printf("%d\n",res);
return 0;
}


记忆化搜索:

#include <stdio.h>
#include <string.h>
#define M 105
int flag[M][M];
int s[M][M];
int m,n;
int around[4][2] = {-1,0,0,-1,1,0,0,1};
int Max(int a,int b){
return a<b?b:a;
}
int dp(int x,int y){
int i,height = 0;
if(flag[x][y])
return flag[x][y];
if(!x||!y||(x==n+1)||(y==m+1))
return 0;
for(i = 0;i<4;i++){
int xx = x+around[i][0];
int yy = y+around[i][1];
if(s[x][y] >s[xx][yy])
height = Max(dp(xx,yy),height);
}
return flag[x][y] = height+1;
}
int main(){
int i,j,max;
scanf("%d %d",&n,&m);
memset(flag,0,sizeof(flag));
memset(s,0,sizeof(s));
for(i = 1;i<=n;i++)
for(j = 1;j<=m;j++)
scanf("%d",&s[i][j]);
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++)
dp(i,j);
for(max = 0,i = 1;i<=n;i++)
for(j = 1;j<=m;j++)
if(flag[i][j] > max)
max = flag[i][j];
printf("%d\n",max);
return 0;
}


2111:题意与1088的区别:不是从高到低,而是从低到高爬坡;爬坡不是向四周,而是走“日”(象棋中的马走日);输出要求按照序列的字典序输出。

思路:仍然是dp没的说,需要考虑怎么输出。一开始怎么也转不过这个弯,认为从低到高更新,那么考虑字典序需要追溯到路径的开始才能比较。后来知道还是从高到低更新即可,这样自然保证了字典序。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s))
#define N 365
int s

,dp

,pre

,n,len;
struct node{
int x,y,h;
}p[N*N];
int ori[8][2] = {{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2}};
int cmp(node a,node b){
return a.h > b.h;
}
int check(int x,int y){
return x>=1&&y>=1&&x<=n&&y<=n;
}
int main(){
int i,j,k,x,y;
len = 0;
clr(dp, 0);
clr(pre, 0);
scanf("%d",&n);
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++){
scanf("%d",&s[i][j]);
p[len].x = i;
p[len].y = j;
p[len++].h = s[i][j];
}
sort(p,p+len,cmp);//按照高度从高到低排序
for(i = 0;i<len;i++){
x = p[i].x;
y = p[i].y;
dp[x][y] = max(dp[x][y],1);//为了更新初始值
for(j = 0;j<8;j++){
int xx = x+ori[j][0];
int yy = y+ori[j][1];
if(check(xx, yy) && s[xx][yy]<s[x][y]){
if(dp[xx][yy] <= dp[x][y]+1){
dp[xx][yy] = dp[x][y]+1;
pre[xx][yy] = j;
}
}
}
}
k = dp[1][1];
x = y = 1;
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++)
if(dp[i][j]>k ||(dp[i][j]==k&&s[i][j]<s[x][y])){
k = dp[i][j];
x = i;
y = j;
}
printf("%d\n",k);
while(k--){
printf("%d\n",s[x][y]);
j = pre[x][y];
x -= ori[j][0];
y -= ori[j][1];
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: