题目描述
题解
算法一
直接暴力 dfs,复杂度指数级,期望得分 10 分。
算法二
用最短路算法,复杂度
O(n2logn)
,根据实现情况可以获得 10 至 30 分。
算法三
只能往下或者往右走这是显然的叭?所以一个二维dp就可以了,时间复杂度
O((2∗n+1)2)
。期望得分30分。
算法四
由于这个正方形是完全中心对称的,所以我们只需要考虑走1/4个就可以了,再加一个滚动数组可以做到
O(n∗(2∗n+1))
,期望得分50分。
算法五
如果从外向里某一层的
ai
为它之前所有
ai
的最小值的话,那么称它为可以成为前缀最小值得一层。
最终最优的路径应该为将所有前缀最小值的层的左上角的点用若干个“L”连接起来所得到的路径。
正确性是显然的。如果要从第一层的左上角到达第i层的任意一点的话,它们中间的每一层都至少经过一次。那么如果有一层不是前缀最小值的话,那么这一层的路径一定可以被它之前的最小值的那一层的路径代替,使答案更优。所以只有能成为前缀最小值的层才可能经过大于1个点,其余的层数都只能经过一个点。
那么,我们可以暴力枚举最深到达的哪一层,最深的一层一定是从左上角进入,走一个“L”形再从右下角出去,每次计算答案,维护最小值即可。显然,最小值一定在枚举的最深的一层是前缀最小值的时候取到。
错误的思路:
1、直接贪心。。。这个贪心陷阱是非常多的,之前想出来的很多种贪心的方法都不科学。比如说小的多走啦,或者大的不走啦,,现在想想都觉得非常傻逼。
2、跑图spfa什么的,完美爆炸。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
#define N 100005
int n,last;
LL a[N],Min,once,f[N],ans;
int main()
{
scanf("%d",&n);
for (int i=n+1;i>=1;--i) scanf("%lld",&a[i]);
Min=a[1]; last=1; ans=(4*n+1)*a[1];
for (int i=2;i<=n+1;++i)
if (a[i]<Min)
{
LL now=f[last]+once+Min*(i-last+1);
ans=min(ans,now*2+a[i]*(4*(n-i+1)+1));
f[i]=f[last]+Min*(i-last+1);
last=i;
Min=a[i];
}
else once+=a[i];
printf("%lld\n",ans);
}
总结
①贪心要敢想,但是也不能胡想。举反栗的能力要加强。想出一种贪心方法了之后要把它当成是别人想的然后尽力去推翻它!不过有的贪心虽然没有严格的证明,但是在实际问题中是可以证明成立的,一定要确保贪心策略的正确性再写!(比如愚蠢的ATP想了4种贪心的思路但是没有一种是对的。——ATP)
②贪心的题目要多分析一下性质。现在分析题目性质的能力还是太辣鸡了。