今天做了2018年蓝桥杯初赛第7题
根据题意,折线的方向为左→上→右→下,上行和下行结束后走的步数都要加一,(注意结果可能很大,要用long long)于是容易写出以下代码:
代码:
#include <cstdio>
#include <algorithm>
#define tru (cx!=n||cy!=m)&&s<maxl
using namespace std;
int main() {
long long n,m,maxl=1,s=0,cx=0,cy=0,t=0;//s为当前步数
long long dis=0;
scanf("%I64d%I64d",&n,&m);
int total_t=max(abs(n),abs(m));
while(cx!=n||cy!=m) {
while(tru) {
cx--;
s++;
dis++;
}
s=0;
while(tru) {
cy++;
s++;
dis++;
}
s=0;
maxl++;
while(tru) {
cx++;
s++;
dis++;
}
s=0;
while(tru) {
cy--;
s++;
dis++;
}
s=0;
maxl++;
}
printf("%I64d",dis);
return 0;
}
这样虽然可以做出,但明显时间复杂度过高,经测试只能通过40%数据,必须设法优化。
优化思路:不必“一步一步”走,完全可以一次性走完一整条水平或竖直线。但这会导致多走,于是要按照上面的思路将多走的部分“走回来”。
代码:
#include <cstdio>
#include <algorithm>
#define tru (cx!=n||cy!=m)&&s<maxl
using namespace std;
int main(){
long long n,m,maxl=1,s=0,cx=0,cy=0,t=0;//s为当前步数
long long dis=0;
scanf("%I64d%I64d",&n,&m);
int total_t=max(abs(n),abs(m));
if(m<0&&abs(m)>=abs(n))
total_t+=1; //考虑下面那条水平线
while(t<total_t){
cx-=maxl;//左
s+=maxl;
dis+=maxl;
s=0;
cy+=maxl; //上
s+=maxl;
dis+=maxl;
s=0;
maxl++;
cx+=maxl;
s+=maxl;
dis+=maxl;
s=0;
cy-=maxl;
s+=maxl;
dis+=maxl;
maxl++;
t++;
}
// printf("%d %d %d\n",cx,cy,maxl);
s=0;
maxl--;
while(cx!=n||cy!=m){
while(tru){ //上
cy++;
s++;
dis--;
}
s=0;
while(tru){ //左
cx--;
s++;
dis--;
}
s=0;
maxl--;
while(tru){
cy--;
s++;
dis--;
}
s=0;
while(tru){
cx++;
s++;
dis--;
}
s=0;
}
printf("%I64d",dis);
return 0;
}
以上代码仍无法通过全部测试数据。经测试在不开优化的条件下的极限数据量约为±3500W少一点,开优化后效率明显提升。
2.20更新:
当然,最好是找到规律,这样连循环都省了,不可能出现超时
#include <cstdio>
#include <algorithm>
using namespace std;
int main(){
long long x,y,maxl=1;
long long dis=0;
scanf("%I64d%I64d",&x,&y);
int t=max(abs(x),abs(y));
if(y<0&&(x>=y&&x<-y))
t+=1; //考虑下面那条水平线,x∈[y,-y)
maxl=t*2;
dis=maxl*(maxl+1);
if(x==t) //以下分别是在右、上、左、下部分的情况
dis-=y-(-t);
else if(y==t)
dis=dis-t*2-(t-x);
else if(x==-t&&y>(-t))
dis=dis-t*4-(t-y);
else if((-y)+1==t){
dis-=t*2*2;
dis=dis-(t*2-1)-(x-(-t));
}
printf("%I64d",dis);
return 0;
}
总结:对于像这种看起来很有规律的题,一定要先找到规律,不要急着做。