L · Loop:中文题面与个人代码
中文题面、约束与样例说明,附个人原始代码分析及本地验证。
本页目录9 节
时限 1 秒,内存限制 512 MB。中文内容依据本次提供的题面整理;以原题为准。
中文题面 #
给定长度为 的非负整数序列 ,构造 方格。第零列自上而下为原序列,此后每一列都是前一列向下循环移动一格。因此第 行、第 列的权值为 。
从左上角 出发,每步只能向右或向下,走到右下角 。路径上每一个经过的格子都计入,包括起点和终点。求最小权值和。
输入、输出与约束 #
。每组输入 ,随后输入 个 。保证所有组的 。每组输出一个整数。路径共有 个格子,必须使用能容纳约 的整数类型。
样例 #
输入 1 #
7
4
3 4 2 1
1
7
5
8 0 9 0 1
7
5 1 9 0 0 9 9
4
1 1000 1000 1
6
4 0 9 0 1 9
3
1000000000 1000000000 1000000000
输出 1 #
13
7
20
30
7
24
5000000000
样例说明 #
第一组一条最优路径经过的权值为 ,和为 。 时只计唯一格子,答案 。最后一组所有格子均为 ,路径有五格,所以答案是 ,超过有符号 32 位整数。
个人代码:把方格路径压成一维往返 #
令 。向下走令 加一,向右走令 减一,当前权值只由 决定。整个过程是一条从零出发又回到零、长为 的一维步行。这样的闭合步行恰有 次加一、 次减一,任意前缀也不会超过最终次数,因此能够还原成合法方格路径。
对一维整数线上的边 ,向两个方向的通过次数相等,记各为 。路径代价可写成“起点权值加每次往返的代价”:
在任意可行步行经过的边中选往返代价最小的一条。保留从零到它所需的各边一次往返,把其余往返都集中到这条最便宜的边,代价不会增大。于是存在一个最优解:先向某一方向到达一条边,在那条边上反复往返,再回到零。枚举这条边与到达方向即可。
对照一基下标的公式 #
代码中 a[1] 对应题面的 ,sum[i] 是前缀和。向正方向到达 a[i] 与 a[i+1] 之间,前面的 条边各往返一次,目标边往返 次,得到
反方向到达同一相邻对时,先经过 a[1],a[n],...,a[i+1],得到
反向公式在 时目标边往返次数为零,代码不枚举它;对应的极端单调往返路线不会优于它经过的某条可重复边,已由其他候选覆盖。跨接 a[1] 与 a[n] 的边单独给出候选 。 时循环为空,最后这个公式自然返回唯一格子的权值。
时间 ,前缀和空间 。最大答案不超过 ,原稿用 long long 足够。
本地结果: 样例通过;923 组数组与独立的 方格最短路 DP 一致,其中长度不超过五、元素取 的数组全部枚举,并补充更长随机数组。 且元素全为 时输出 ,符合路径格子数。
原始 C++ 文件 #
下面展示原稿,保留未完成部分和已发现问题;下载文件保留原始字节。SHA-256:404409239ef1a35e6e2a9296d5d31636f995b7f67e5efca903f42a593c129a81。
#include<bits/stdc++.h>
#define int long long
const int inf=1e18;
using namespace std;
int read(){
int x;scanf("%lld",&x);
return x;
}
int n,a[100005],sum[100005];
void solve(){
n=read();
for(int i=1;i<=n;i++)a[i]=read(),sum[i]=sum[i-1]+a[i];
int ans=inf;
for(int i=1;i<n;i++){
int cnt=n-1-(i-1);
ans=min(ans,(a[i]+a[i+1])*cnt+sum[i-1]*2+a[i]);
if(i!=1){
cnt=n-1-(n-i);
ans=min(ans,(a[i]+a[i+1])*cnt+(sum[n]-sum[i+1]+a[1])*2+a[i+1]);
}
// cout<<i<<" "<<ans<<" "<<cnt<<endl;
}
ans=min(ans,(a[1]+a[n])*(n-1)+a[1]);
printf("%lld\n",ans);
}
signed main(){
int t=read();
while(t--)solve();
}
讨论
评论
正在加载评论…