POJ 1088 滑雪

点击打开链接


题目意思:给定一个 r 行 c列的山峰,有个人在里面滑雪,他只能从高的向低的滑,(不能向相同的),求这个人可以滑的最长的长度.


解题思路:我们有两种方法做 , 第一是递归搜索,第二是DP, 下面贴上我的代码有解释


代码1(DP):


/*我们可以对输入的数据存到一个temp数组里面,然后对它排序,枚举数组中的每一个点,我们知道对于一个点,能够影响到它的肯定是比它小的点,那么我们只要能够找到该店周围比它小的点的最大值加一即是该点的最长路径,这就是动态规划的思路*/

#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <stack>
#include <list>
#include <algorithm>
using namespace std;
const int MAXN = 1000010;

int ans[110][110] , h[110][110] , temp[MAXN] , mark[110][110];
//ans数组存储对应点的最长路径  temp存储所有高度 mark标记是否搜索过该点(如果输入有相同元素时候会判断)
int r , c , Max;
int x[4] = {-1,0,1,0} , y[4] = {0,1,0,-1} , tempans[4];//tempans是临时存储周围四个点的最长的长度

//判断函数
int maxpath(int n , int i , int j){
    int k , tempmax = 0;
    fill(tempans , tempans+4 , 1);//注意这里要初始化为1,假如周围四点都比它大则返回1即可
    for(k = 0 ; k < 4 ; k++){//遍历四点
        if(i+x[k]<0 || i+x[k]>= r || j+y[k]<0 || j+y[k]>=c)//超过边界
            continue;
        if(h[i+x[k]][j+y[k]] >= n)//比它大则继续
            continue;
        tempans[k] =  ans[i+x[k]][j+y[k]] + 1;//对应点加一
    }
    for(k = 0 ; k < 4 ; k++){//找出四个方向最大值
        if(tempmax < tempans[k])
            tempmax = tempans[k];
    }
    return tempmax;
}
//处理问题函数
void solve(){
    int i , j , k , count , Max = 0;
    i = c*r  ; count = 0;
    sort(temp , temp+i);//排序
    memset(ans , 0 , sizeof(ans));//初始化为0
    memset(mark , -1 , sizeof(mark));//mark初始化为-1
    for(k = 0 ; k < c*r ; k++){
        count = 0;
        for(i = 0 ; i < r ; i++){
            for(j = 0 ; j< c ; j++){
                if(mark[i][j] == 1)
                    continue;
                if(temp[k] == h[i][j] && mark[i][j] == -1){
                   ans[i][j] = maxpath(h[i][j] , i , j); //求出最大值
                   mark[i][j] = 1; count = 1;//并且把带点标记为1说明遍历过,防止重复遍历
                   if(Max<ans[i][j])
                       Max = ans[i][j];//找到最大值
                   break;//找到一点就退出
                }
            }
            if(count)//count说明这一行是否找到,如果找到为1 退出
                break;
        }
    }
    cout<<Max<<endl;//输出最大值
}
//主函数
int main(){
    int k;
    while(cin>>r>>c){
        k = 0;
        for(int i= 0 ; i < r ; i++){
            for(int j = 0 ; j < c ; j++){
                scanf("%d" ,&h[i][j]);
                temp[k] = h[i][j];
                k++;
            }
        }
        solve();
    }
    return 0;
}
代码2(dfs):

#include <cctype>
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <stack>
#include <list>
#include <algorithm>
using namespace std;

int h[110][110] , mark[110][110];//用一个mark数组来表示该点是否被求过并且存储它的最长路径值
int c , r , Max , sum;
int x[4] = {-1,0,1,0} , y[4] = {0,1,0,-1};//方向对应的坐标的变化,从上开始顺时针

//递归函数(dfs)
int longpath(int n , int i , int j){
    int k , max = 0, temp = 0;//注意初始化为0
    if(mark[i][j] != -1)//如果当前点被求过就返回
        return mark[i][j];
    for(k = 0 ; k < 4 ; k++){//往四个方向遍历
        if(i+x[k]<0 || i+x[k]>= r || j+y[k]<0 || j+y[k]>=c)//如果超过边界则继续下一个方向
            continue;
        if(h[i+x[k]][j+y[k]] >= n)//如果下一点高度大于当前点则不用考虑
            continue;
        temp = longpath(h[i+x[k]][j+y[k]] , i+x[k] , j+y[k]);//递归,用temp来存储下一方向的最长路径
        if(temp > max)
            max = temp;//更新四个方向中的最大值
    }
    mark[i][j] = max + 1;//这里要加一,因为 2-1 算2 ,开始的初始化为0
    return mark[i][j];//返回该点的最长路径
}
//处理问题函数
void solve(){
    int i , j , tempmax , Max = 0;
    for(i = 0 ; i < r ; i++){        
        for(j = 0 ; j < c ; j++){
            tempmax = longpath(h[i][j] , i , j);//对于每一个点我们去找到该店的最长路径
            if(Max < tempmax)
                Max = tempmax;//更新最大值
        }
    }
    printf("%d\n" , Max);
}
//主函数
int main(){
    int i , j , k , n;
    cin>>n;
    while(n--){
        while(cin>>r>>c){
            sum = 0;k = 0;
            memset(h , -1 , sizeof(h));//初始化高度为-1
            memset(mark , -1 , sizeof(mark));//这里初始化为-1
            for(i = 0 ; i < r ; i++){
                for(j = 0 ; j< c ; j++){
                scanf("%d" , &h[i][j]);
                sum += h[i][j];
                }
            }
            if(sum == 0)//全部是0直接输出1
                cout<<1<<endl;
            else
                solve();
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值