Vijos P1006 晴天小猪历险记

背景 Background

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

描述 Description

  这一天,他来到了一座深山的山脚下,因为只有这座深山中的一位隐者才知道这种药草的所在。但是上山的路错综复杂,由于小小猪的病情,晴天小猪想找一条需时最少的路到达山顶,但现在它一头雾水,所以向你求助。
  山用一个三角形表示,从山顶依次向下有1段、2段、3段等山路,每一段用一个数字T(1<=T<=100)表示,代表晴天小猪在这一段山路上需要爬的时间,每一次它都可以朝左、右、左上、右上四个方向走(**注意**:在任意一层的第一段也可以走到本层的最后一段或上一层的最后一段)。
  晴天小猪从山的左下角出发,目的地为山顶,即隐者的小屋。

输入格式 Input Format

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

输出格式 Output Format

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

样例输入 Sample Input

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

样例输出 Sample Output

10

这道题的方程很好写,f[i,j]:=min{f[i-1,j],f[i-1,j-1],f[i,j-1],f[i,j+1]);

相信很多人写出这个方程后,就因为对于这个方程要用到后面的状态,就说它有后效性,其实我们仔细分析,这道题仍然符合无后效性,对样每一层,止于上一层有关,但对于每一层,我们不能只循环一次,我们用一个repeat,如果某个状态改变,那么这次层的某个状态可能也跟着改变,所以循环到搜一边后,这一层的各个点的状态没有改变而止。我把这样的dp叫暴力dp

还有一个预处理,搜每一层之前,把f[i,0]:=f[i,i]; f[i,i+1]:f[i,1];

代码:

program pig;
var
    f,a:array[0..1005,0..1005] of longint;
    i,j,min,n:longint;
    flag:boolean;
begin
    assign(input,'hill2.in');
    reset(input);
    assign(output,'pig.out');
    rewrite(output);

    readln(n);
    for i:=1 to n do
     for j:=1 to i do
      read(a[i,j]);
    fillchar(f,sizeof(f),127);
    f[1,1]:=a[1,1];
    for i:=2 to n do
     begin
       repeat
        flag:=true;
         f[i,0]:=f[i,i]; f[i,i+1]:=f[i,1];
         for j:=1 to i do
          begin
            if f[i,j]>f[i,j-1]+a[i,j] then begin
                                      flag:=false;
                                      f[i,j]:=f[i,j-1]+a[i,j];
                                    end;
            if f[i,j]>f[i,j+1]+a[i,j] then begin
                                       flag:=false;
                                       f[i,j]:=f[i,j+1]+a[i,j];
                                     end;
            if f[i,j]>f[i-1,j]+a[i,j] then begin
                                      flag:=false;
                                      f[i,j]:=f[i-1,j]+a[i,j];
                                    end;
            if f[i,j]>f[i-1,j-1]+a[i,j] then begin
                                        flag:=false;
                                        f[i,j]:=f[i-1,j-1]+a[i,j];
          end;                             end;

       until flag;
     end;
    min:=maxlongint;
    writeln(f[n,1]);

    close(input);
    close(output);
end.

C++

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[2000][2000],a[2000][2000];
int main()
{
    int n;
    memset(dp,63,sizeof(dp));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    dp[1][1]=a[1][1];
    for(int i=2;i<=n;i++)
    {
        while(1)
        {
            bool flag=false;
            int tmp;
            dp[i][0]=dp[i][i];
            dp[i][i+1]=dp[i][1];
            for(int j=1;j<=i;j++)
            {
                tmp=min(dp[i][j],min(min(min(dp[i-1][j],dp[i-1][j-1]),dp[i][j-1]),dp[i][j+1]));
                if(tmp+a[i][j]<dp[i][j])
                {
                    dp[i][j]=tmp+a[i][j];
                    flag=true;
                }
            }
            if(flag==false) break;
        }
    }
    printf("%d",dp[n][1]);
    return 0;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值