蒟蒻求助!70分,dp的方法(三十行代码,有注释

P1434 [SHOI2002] 滑雪

xixi_bang @ 2020-04-12 20:42:10

#include<bits/stdc++.h>
using namespace std;
long long maze[105][105];//表示滑雪点高度
long long dp[105][105];
int main(){
    int R,C;
    cin>>R>>C;
    for(int i=1;i<=R;i++)
    for(int j=1;j<=C;j++){
        cin>>maze[i][j];
    }
    for(int i=1;i<=R;i++)
    for(int j=1;j<=C;j++){//dp[i][j]表示在(i,j)位置时能够得到的最大滑雪高度
        int a=0,b=0,c=0,d=0;
        int h=maze[i][j];

        if(i+1<=R&&maze[i+1][j]<maze[i][j]) a=1;
        if(j+1<=C&&maze[i][j+1]<maze[i][j]) b=1;
        if(i-1>=1&&maze[i-1][j]<maze[i][j]) c=1;
        if(j-1>=1&&maze[i][j-1]<maze[i][j]) d=1;
        //利用a,b,c,d判断max值是否有效
        dp[i][j]=max(a*(dp[i+1][j]+h-maze[i+1][j]),max(b*(dp[i][j+1]+h-maze[i][j+1]),
                max(c*(dp[i-1][j]+h-maze[i-1][j]),d*(dp[i][j-1]+h-maze[i][j-1]))));
    }
    long long ans=0;
    for(int i=1;i<=R;i++)
    for(int j=1;j<=C;j++){
        ans=max(dp[i][j],ans);
    }

    cout<<ans+1<<endl;//高度最后需要+1
}

by metaphysis @ 2020-04-12 21:51:25

@xixi_bang

我想您应该是理解错题意了。

输出区域中最长滑坡的长度。

我不太清楚照您这样写最后输出的是什么(最大的高度差之和?)。


by xixi_bang @ 2020-04-13 12:31:25

@metaphysis 是的,我理解成最大高度差了,但是如果我把h-maze[i][j]替换成+1为什么也行不通呢

 dp[i][j]=max(dp[i][j],max(a*(dp[i+1][j]+1),max(b*(dp[i][j+1]+1),
                max(c*(dp[i-1][j]+1),d*(dp[i][j-1]+1)))));

by metaphysis @ 2020-04-13 12:41:51

@xixi_bang

这样还不行。举个例子:

2 2
3 4
2 1

当你计算高度为3的格子的最大滑雪长度时,由于正下方的格子高度为2,小于3,此时你会得到高度为3的格子的最大滑雪长度为1,然后高度为3的格子就不会再更新了。但是实际上高度为2的格子的最大滑雪长度为2。

也就是说,后续更新的值,您无法知晓,需要使用 DFS 来确定。


by metaphysis @ 2020-04-13 12:43:11

DFS + 备忘。


by xixi_bang @ 2020-04-13 12:59:24

@metaphysis 可以利用优先队列排列之后再进行dp?


by metaphysis @ 2020-04-13 13:04:54

@xixi_bang

您具体描述下思路看看,只要能够保证当今格子的最大滑雪长度是最优值即可,不论你使用何种方法。


by xixi_bang @ 2020-04-13 13:08:15

@metaphysis

#include<bits/stdc++.h>
using namespace std;
long long maze[105][105];//表示滑雪点高度
long long dp[105][105]={1};
struct pix{
    int x=0,y=0;
    int height=0;
};
struct cmp{
bool operator()(pix& a,pix& b){
    if(a.height!=b.height)
    return a.height>b.height;
    else
        return a.x<b.x;
}
};

int main(){
    int R,C;
    cin>>R>>C;
    for(int i=1;i<=R;i++)
    for(int j=1;j<=C;j++){
        cin>>maze[i][j];
    }
    priority_queue<pix,vector<pix>,cmp> pri;
    for(int i=1;i<=R;i++)
    for(int j=1;j<=C;j++)
    {
        pix t;
        t.x=i;
        t.y=j;
        t.height=maze[i][j];
        pri.push(t);

    }
    long long ans=0;
    while(pri.empty()==false){
        pix f=pri.top();
        pri.pop();
        int i=f.x;
        int j=f.y;
        int a=0,b=0,c=0,d=0;
        if(i+1<=R&&maze[i+1][j]<maze[i][j]) a=1;
        if(j+1<=C&&maze[i][j+1]<maze[i][j]) b=1;
        if(i-1>=1&&maze[i-1][j]<maze[i][j]) c=1;
        if(j-1>=1&&maze[i][j-1]<maze[i][j]) d=1;
        //利用a,b,c,d判断max值是否有效
        dp[i][j]=max(dp[i][j],max(a*(dp[i+1][j]+1),max(b*(dp[i][j+1]+1),
                max(c*(dp[i-1][j]+1),d*(dp[i][j-1]+1)))));
        ans=max(dp[i][j]+1,ans);
    }

    cout<<ans<<endl;//高度最后需要+1
}

像是这样,有个问题,如何考虑到这种dp需要进行排序后再进行状态转移的问题呢,还有就是dp如何debug呢,我时常是靠自己去想来理解逻辑问题,但是不知道怎么来通过状态变更来找到逻辑的错误


by metaphysis @ 2020-04-13 15:14:40

嗯,我晚上帮您看看,但直觉上觉得可能会行不通,因为排序不是必需的一个步骤。DP就怕大方向想错了,大方向一想错,很难推出递推关系式,递推关系式一错,后面跟着错。


by metaphysis @ 2020-04-13 15:19:10

嗯,似乎是行得通的,因为要求高度严格递减,用优先队列保证了低的高度先处理,你可以自己先构造几组简单的测试数据,看看结果如何。


by metaphysis @ 2020-04-13 19:14:45

@xixi_bang

使用优先队列能够保证最优解,原因前述已经说明,根本原因在于题目约束要求高度要严格递减。对于原网格来说,其递推关系式为:

dp[i][j] = max{dp[x][y] + 1},|i-x|+|j-y|=1,h[x][y]<h[i][j]

使用优先队列保证了递推关系式的正确性。应该说,使用优先队列是根据递推关系式得到的一种实现,而不是说先使用优先队列排序后,在来推导递推关系式。


| 下一页