题解:P11296 [NOISG2018 Prelim] Snail
题目链接
分析
很巧妙的一道题,细节也很多。
事先说明:\(h\)、\(p\)、\(n\) 与题目意思相同,\(sum\) 表示每天总位移。
不妨考虑下面情况:
5 2
6 -4
此时为直接枚举每天爬行。
注意:不能通过计算 \(sum\) 通过直接 \(h \div sum\) 来计算天数。
因为在这种情况下,可能因为先前爬出去了后面又退回来。
那如何解决呢?
不妨通过枚举前 \(2\) 天的位移,在这里我们定义 \(i\) 为第 \(i\) 个阶段(总的)
\(mn[i]\) 来表示在第 \(i\) 天前的总位移。
那么我们可以推出
\[mn[i+1]=mn[i] + p[i]
\]
但此时要注意 \(p[i]\) 有可能为负,倒退回原点甚至更远,且 \(0 \le i < 2 \times n\)。
所以应该改为
\[mn[i+1] = \max(mn[i]+p[i],0)
\]
接下来就是简单的模拟实现了。
实现
具体内容都在注释,不过多赘述。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=9223372036854775807;
ll h,n,p[10005],sum,mn[20005];
int main(){
cin>>h>>n;
for(int i=0;i<n;i++){
cin>>p[i];
sum+=p[i];//统计一天内总位移
}
//mn[i]指在第i次操作时之前的总位移
for(int i=0;i<2*n;i++){//枚举前两天的操作数
mn[i+1]=max(mn[i]+p[i%n],0LL);//第i+1次操作时之前的总位移
if(mn[i+1]>=h){
cout<<i/n<<' '<<i%n;//第i/n天,第i%n阶段
return 0;
}
}
//在此时判断是否一天的位移为负数
//因为会出现走出去后又走回去导致无法出去的情况
//而在第一天内就会走出去的情况在46行实现
if(sum<=0){
cout<<"-1 -1";
return 0;
}
//模拟爬行
ll ansday=INF,jieduan=0;
for(int i=0;i<n;i++){
ll days=1+(h-mn[n+i+1]+sum-1)/sum;//计算
if(ansday>days){
ansday=days;
jieduan=i;
}
}
cout<<ansday<<' '<<jieduan;
return 0;
}