[DP][vijos1006]晴天小猪历险记之Hill

题目梗概

首先题目和数字三角形那道经典的DP题目描述一样,不过增加了两处不同:

1.同行之间可以相邻移动

2.位置(i,j)(i,j),它与(i-1,j-1)(i1,j1)以及(i-1,j)(i1,j)相邻,特别的(i,1)(i,1)与(i-1,i-1)(i1,i1)相邻,且(i,i)(i,i)与(i-1,1)(i1,1)相邻.

 

思考

首先第一点我们只要在数字三角形的基础上,加上每行的移动就可以了。

但是第二点就要注意了,这样的描述使DP变成了环形。

具体的解释看这位大佬(大佬的想法)

f[i][j]表示第i行第j个点到目标终点(1,1)的最小时间
则转换为数字三角形问题,但是只是多了几种走法,不断更新最小值就好了
但是问题就来了,这样动态规划具有最优子结构吗?
注意这是个环形走法
答案是不成立于的,怎么说?
我们来看一下这样一个例子,假设某个数据的第某层的时间为
1,1,1,1,1,1
而从下往上推上来一开始的初值f[][]分别为
1,2,4,3,9,10
那么我们先进行第一次同行内从左往右的更新的递推(可以看代码内的推法)
则有更新为
1,2,3,3,4,5
再从右往左更新推一遍
1,2,3,3,4,2(左端的1可以走到右端来更新了右端的时间)
那么这样就完了吗?不,我们可以发现我们可以用新更新的2去更新推出更优的解
则应该为
1,2,3,3,3,2
所以从这个样例中我们可以看出一次两边推根本的不出最优解
为什么呢?
我们看某次往一边推,由于是环形,所以可能从右向左推,用第一个更新了最右端的那个点
但是最右端的那个点在更新之前已经推完了右边数的第二个点
就是新更新的这个右端点并没有用来当作"下家"来更新别的点使别的点更优
同理从左往右也是一样
那么怎么办呢?
我们可以推两遍,这样假如更新了某个端点的值,在下一次递推时一定能用来作为"下家"尝试再更新别的点
那么这样问题就解决了
我们总结一下做法
首先每个点的初值为从下一层走到这一层的两个更优解
然后我们在同层迭代递推,左推一遍右推一遍,然后再重复推一遍

问题就解决了,so easy.

 

代码实现:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn=1005;
int a[maxn][maxn];
int f[maxn][maxn];
int n;

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        cin>>a[i][j];
    f[1][1]=a[1][1];//终点处就直接是该点时间
    for(int i=2;i<=n;i++)//一层一层往上推
    {
        for(int j=2;j<i;j++)//先求出从上一层推出来的最小值
            f[i][j]=min(f[i-1][j],f[i-1][j-1])+a[i][j];
        f[i][1]=min(f[i-1][1],f[i-1][i-1])+a[i][1];//特殊边界点处理
        f[i][i]=min(f[i-1][i-1],f[i-1][1])+a[i][i];//特殊边界点处理
        //同一层更新最优解
        for(int k=i-1;k>0;k--)//从右往左推 从右往左走的情况更新
            f[i][k]=min(f[i][k],f[i][k+1]+a[i][k]);
        f[i][i]=min(f[i][i],f[i][1]+a[i][i]);

        for(int l=2;l<=i;l++)//从左往右推 从左往右走的情况更新
            f[i][l]=min(f[i][l],f[i][l-1]+a[i][l]);
            f[i][1]=min(f[i][1],f[i][i]+a[i][1]);

        for(int k=i-1;k>0;k--)//再推一遍从右往左推 从右往左走的情况更新
            f[i][k]=min(f[i][k],f[i][k+1]+a[i][k]);
            f[i][i]=min(f[i][i],f[i][1]+a[i][i]);

        for(int l=2;l<=i;l++)//再推一遍从左往右推 从左往右走的情况更新
            f[i][l]=min(f[i][l],f[i][l-1]+a[i][l]);
                f[i][1]=min(f[i][1],f[i][i]+a[i][1]);
    }
    cout<<f[n][1]<<endl;
}

 

转载于:https://www.cnblogs.com/OIerLYF/p/7260700.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值