一道省选的动态规划竟能如此简单的AC掉?

17 篇文章 0 订阅
2 篇文章 0 订阅

今天酉时,不才蒟蒻正在洛谷的题海中迷茫的时候,找到了这样一道题:

 但其实自己看并不难,不要被他的标签唬住了

题目描述

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

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 24-17-16-1(从 24 开始,在 1结束)。当然 25-24-23-…-3-2-1 更长。事实上,这是最长的一条。 
 

输入格式

输入的第一行为表示区域的二维数组的行数 R 和列数 C。下面是 R 行,每行有 C 个数,代表高度(两个数字之间用 1 个空格间隔)。

输出格式

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

样例输入1
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
样例输出1
25

提示/说明

对于 %100的数据,1≤R,C≤100。 

他这个题目描述的属实难受看不懂

抽象一下,就一句话

求二维最长下降子序列

 那就简单了

最长不下降子序列会吧?

就剩下一个二维了,也确实是这道题的难点

注意看,数据范围贼拉小

那不就直接暴力了吗

二维暴力是什么?

不就是深搜吗?

动态规划三要素:三要素没听过的或者不太懂的点我

初始化:memset

状态定义:dp[i][j]代表这个位置为起点的滑坡最多为多少

状态转移方程:一会儿再说

主函数:

scanf("%lld%lld",&r,&c);
	for (int i=1;i<=r;i++){
		for (int j=1;j<=c;j++){
			scanf("%lld",&map[i][j]);
		}
	}
	memset(dp,0,sizeof(dp));
	for (int i=1;i<=r;i++){
		for (int j=1;j<=c;j++){
			dp[i][j]=dfs(i,j);
			if (dp[i][j]>cnt){
				cnt=dp[i][j];
			}
		} 
	}
	printf("%lld",cnt);

那就下来就到重点的地方

dfs函数:

int dfs(int x,int y){
	int now=1;
	for(int i=0;i<4;i++){
    	int tx=x+dxy[i][0];
    	int ty=y+dxy[i][1];
    	if(tx>=1&&tx<=r&&ty>=1&&ty<=c&&map[x][y]>map[tx][ty]){
    	    int temp=dfs(tx,ty)+1;
    	    if(temp>now){
    	        now=temp;
    	    }
   		}

	}
	return now;
}

注意看for循环内部第四行

为什么要加一呢?

因为他滑坡下去,路程得算上起点吧?

提交上去

喜提TLE90分

主要的说明没问题了,接下来就是优化了

dp没什么可以优化的

那就优化dfs

要用到剪枝

加一个特判就行,如果dp[x][y]不为0,也就是说已经计算过了,那就直接返回

ac代码;

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
# define int long long
int r,c,cnt;
int map[105][105];
int dp[105][105];
int dxy[5][2]={{1,0},{-1,0},{0,1},{0,-1}};
int dfs(int x,int y){
	if (dp[x][y]){
		return dp[x][y];
	}
	int now=1;
	for(int i=0;i<4;i++){
    	int tx=x+dxy[i][0];
    	int ty=y+dxy[i][1];
    	if(tx>=1&&tx<=r&&ty>=1&&ty<=c&&map[x][y]>map[tx][ty]){
    	    int temp=dfs(tx,ty)+1;
    	    if(temp>now){
    	        now=temp;
    	    }
   		}

	}
	return now;
}
signed main(){
	scanf("%lld%lld",&r,&c);
	for (int i=1;i<=r;i++){
		for (int j=1;j<=c;j++){
			scanf("%lld",&map[i][j]);
		}
	}
	memset(dp,0,sizeof(dp));
	for (int i=1;i<=r;i++){
		for (int j=1;j<=c;j++){
			dp[i][j]=dfs(i,j);
			if (dp[i][j]>cnt){
				cnt=dp[i][j];
			}
		} 
	}
	printf("%lld",cnt);
	return 0;
}

ac记录:

如果你觉得不才蒟蒻讲的不错,就点一个免费的赞吧 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值