洛谷 P1434 [SHOI2002]滑雪
题目描述
Michael 喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael 想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:
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
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 2424-1717-1616-11(从 2424 开始,在 11 结束)。当然 2525-2424-2323-ldots…-33-22-11 更长。事实上,这是最长的一条。
输入格式
输入的第一行为表示区域的二维数组的行数 RR 和列数 CC。下面是 RR 行,每行有 CC 个数,代表高度(两个数字之间用 11 个空格间隔)。
输出格式
输出区域中最长滑坡的长度。
输入输出样例
输入 #1复制
输出 #1复制
说明/提示
对于 100%100% 的数据,1leq R,Cleq 1001≤R,C≤100。
题解:
作为一道记忆化搜索的题,我硬是拿DP做的。
作为本蒟蒻恢复训练后的第一道题
一开始想的很简单,非常容易得知状态是(dp[i][j])表示到达((i,j))点的最长滑雪路径,最后从头到尾统计一遍最大的(dp[i][j])就是答案了。
然后开始转移,就是如果((i,j))周围的四个点比这个点高(因为只有高的能往低处滑),就更新(dp[i][j])取最大就可以。
然后挂。
百思不得其解(果然半年停训停傻了)我觉得思路比较完美,因为是一道黄题,觉得思维强度也就应该是这水平(逃)
后来发现,我直接扫描的转移不是最优的。就是因为转移的时候还没有计算的那部分有可能更优,然而还没来得及被计算。
就像这样:(显示的是DP数组)
2 5 3 6 9 8
2 6 6 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
当前转移到第二行第四列,但是只能从上、左两个状态转移得到,事实上右、下的状态有可能更优。所以导致答案统计不全。
那么怎么办呢?
这就涉及到了DP顺序的问题。以我看来,DP顺序需要满足两种条件:第一种,转移最优,第二种,无后效性。
我们分析问题容易得出,如果先统计低的,后统计高的,那么就不会有转移上的问题。在当前点,只有比它低的能转移到它。
所以把矩阵所有的值进行排序,然后用结构体构建映射。最后按照这个顺序来转移就可以。整个过程可以用优先队列实现。
代码:
#include<cstdio>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=110;
int n,m,ans;
int map[maxn][maxn];
int dp[maxn][maxn];//dp[i][j]表示(i,j)点的最长路
struct node
{
int x,y,val;
}a[10010];
int tot;
struct cmp
{
bool operator()(node x,node y)
{
return x.val<y.val;
}
};
priority_queue <node,vector<node>,cmp> q;
void judge(int x,int y)
{
if(map[x-1][y]>map[x][y])
dp[x][y]=max(dp[x][y],dp[x-1][y]+1);
if(map[x+1][y]>map[x][y])
dp[x][y]=max(dp[x][y],dp[x+1][y]+1);
if(map[x][y-1]>map[x][y])
dp[x][y]=max(dp[x][y],dp[x][y-1]+1);
if(map[x][y+1]>map[x][y])
dp[x][y]=max(dp[x][y],dp[x][y+1]+1);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
dp[i][j]=1;
a[++tot].val=map[i][j];
a[tot].x=i;
a[tot].y=j;
q.push(a[tot]);
}
while(!q.empty())
{
node now=q.top();
int xx=now.x;
int yy=now.y;
judge(xx,yy);
q.pop();
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,dp[i][j]);
printf("%d",ans);
return 0;
}