【原创】【动态规划】Vijos P1006 晴天小猪历险记之Hill

晴天小猪冒险记之Hill

题目

背景

在很久很久以前,有一个动物村庄,那里是猪的乐园(^_^),村民们勤劳、勇敢、善良、团结……
不过有一天,最小的小小猪生病了,而这种病是极其罕见的,因此大家都没有储存这种药物。所以晴天小猪自告奋勇,要去采取这种药草。于是,晴天小猪的传奇故事便由此展开……
描述

这一天,他来到了一座深山的山脚下,因为只有这座深山中的一位隐者才知道这种药草的所在。但是上山的路错综复杂,由于小小猪的病情,晴天小猪想找一条需时最少的路到达山顶,但现在它一头雾水,所以向你求助。
山用一个三角形表示,从山顶依次向下有1段、2段、3段等山路,每一段用一个数字T(1<=T<=100)表示,代表晴天小猪在这一段山路上需要爬的时间,每一次它都可以朝左、右、左上、右上四个方向走。山是环形的。(注意:在任意一层的第一段也可以走到本层的最后一段或上一层的最后一段)。
晴天小猪从山的左下角出发,目的地为山顶,即隐者的小屋。
★★★本题为vijos早年陈题,描述晦涩,现重新描述题面如下★★★
有一个数字三角形,共nn行,依次编号为第一行,第二行至第nn行。其中第ii行有ii个数字,位置依次记为(i,1),(i,2)(i,1),(i,2)到(i,i)(i,i)。
现在从第nn层的第一个位置出发(即(n,1)(n,1)),每一步移到相邻的,且行编号小于或等于当前行编号的一个位置中,直到(1,1)(1,1)结束,在不重复经过任何位置的情形下,路过的所有位置(包括端点)的对应数字之和最小。
下面详细定义相邻关系。
同一层内连续的两个位置相邻,特别的有每一层第一个位置与最后一个位置相邻。
对于位置(i,j)(i,j),它与(i-1,j-1)(i−1,j−1)以及(i-1,j)(i−1,j)相邻,特别的(i,1)(i,1)与(i-1,i-1)(i−1,i−1)相邻,且(i,i)(i,i)与(i-1,1)(i−1,1)相邻。
格式

输入格式

第一行有一个数n(2<=n<=1000),表示山的高度。
从第二行至第n+1行,第i+1行有i个数,每个数表示晴天小猪在这一段山路上需要爬的时间。
输出格式

一个数,即晴天小猪所需要的最短时间。
样例1

样例输入1

5
1
2 3
4 5 6
10 1 7 8
1 1 4 5 6

样例输出1

10
限制

各个测试点1s
提示

在山的两侧的走法略有特殊,请自己模拟一下,开始我自己都弄错了……
来源

Sunnypig

题意

分析

乍一看是一道数字金字塔。
再一看居然还能左右走!
再一看山居然是环形!
也就是说,我们可以从某排的第一个,走到上一排的最后一个或是这一排的最后一个。
怎么做呢?

对于每一行:
首先按数字金字塔筛一遍,记得特判一下边界。
然后就该左右走了。
但是,假设你在第一个格子往右走,但是你第一个格子可能是最后一个格子往右走得来的,这样更新到了最后一个格子,也就是说,最后一个格子的更新取决于最后一个格子的值!
就是一个先有鸡还是先有蛋的问题了!

《数学之美》上也有类似的问题。
google要做一个网站排名。怎么排名?谁被其他网站链接的多,谁的排名就高。但是,有网站就会恶意刷排名,创造小号网站刷链接。因此程序员们想到一个办法——排名高的网站的链接更有价值。
看起来很棒,但是算排名的时候要用到排名?
于是,有程序员又想出了办法——迭代两次

至于为什么,我也不知道。2333

所以这道题也可以迭代两次。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define INF 0x7ffffabc
#define MAXN 1007

void Read(int &p)
{
    p=0;
    int f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
        p=p*10+c-'0',c=getchar();
    p*=f;
}

int N,mp[MAXN][MAXN],dp[MAXN][MAXN];

void debug()
{
    for(int i=1;i<=N;i++,putchar('\n'))
        for(int j=1;j<=i;j++)
            if(dp[i][j])printf("%d ",dp[i][j]);
}

int main()
{
    Read(N);
    for(int i=1;i<=N;i++)
        for(int j=1;j<=i;j++)
            Read(mp[i][j]);
    dp[1][1]=mp[1][1];
    for(int i=2;i<=N;i++)
    {
        for(int j=2;j<i;j++)
            dp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+mp[i][j];
        dp[i][1]=min(dp[i-1][1],dp[i-1][i-1])+mp[i][1];
        dp[i][i]=min(dp[i-1][i-1],dp[i-1][1])+mp[i][i];
        for(int t=1;t<=2;t++)
        {
            for(int j=i-1;j>=1;j--)
                dp[i][j]=min(dp[i][j],dp[i][j+1]+mp[i][j]);
            dp[i][i]=min(dp[i][i],dp[i][1]+mp[i][i]);

            for(int j=2;j<=i;j++)
                dp[i][j]=min(dp[i][j],dp[i][j-1]+mp[i][j]);
            dp[i][1]=min(dp[i][1],dp[i][i]+mp[i][1]);
        }
    }
    printf("%d\n",dp[N][1]);
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值