标签:端点 ram iostream ios 总结 移动 https user ace
首先题目和数字三角形那道经典的DP题目描述一样,不过增加了两处不同:
1.同行之间可以相邻移动
2.位置(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)相邻.
首先第一点我们只要在数字三角形的基础上,加上每行的移动就可以了。
但是第二点就要注意了,这样的描述使DP变成了环形。
具体的解释看这位大佬(PowderHan大佬的想法)
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; }
标签:端点 ram iostream ios 总结 移动 https user ace
原文地址:http://www.cnblogs.com/OIerLYF/p/7260700.html