CROC-MBTU 2012, Final Round (Online version, Div. 2)笔记
感觉这次的题目几乎没有算法,都是实现有点烦的题目,代码能力还有待提高.
题意:
一个平面上有4个点A,B,C,D,A点坐标确定为(0,0),B点坐标为(a,y1),C点坐标为(B,y2),C到D点的距离确定为L,求选取哪个y1和y2的组合可以使总距离最小.
赛时想法:
当时的情况是C题写的有点慢了,D题剩下40分钟不到,不管是不是心急吧,反正图画出来以后没有什么明确的想法...看着10W的M和N,大脑处于基本空白的状态...坚持到还有20分钟的时候就暂时放弃了...想着就算有了思路也不一定有时间实现了,然后去看别人前面的代码,想要hack赚点分...不过hack还是风险太大了,这次才知道在比赛中看别人的代码是不能复制的,所以样例也没法测试,最后也没有hack,所以总结起来就是最后的时间是被我浪费掉了.
正确思路:
结束以后抱着强烈的好奇心就去看别人的解法了...看完第一个人的就傻了...比赛的时候一直想着,由于L是不确定的,所以不能直接算...可是完全没有往枚举y2的方向去想,恍然大悟了...其实就是枚举y2的坐标,然后三分确定y1的坐标.
代码1:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> #include<math.h> #define maxn 110000 int va1[maxn],va2[maxn],l[maxn]; double dis(int x,int y) { return sqrt(1.0*x*x+1.0*y*y); } int main() { int n,m,a,b,lans,rans,i,j,mid1,mid2; double min=10000000000; while(scanf("%d%d%d%d",&n,&m,&a,&b)!=EOF) { for(i=1;i<=n;i++) { scanf("%d",&va1[i]); } for(i=1;i<=m;i++) { scanf("%d",&va2[i]); } for(i=1;i<=m;i++) { scanf("%d",&l[i]); int st=1,end=n; while(st<end-3) { mid1=(st+end)/2,mid2=mid1+1; double ans1=dis(a,va1[mid1])+dis(a-b,va1[mid1]-va2[i]); double ans2=dis(a,va1[mid2])+dis(a-b,va1[mid2]-va2[i]); if(ans1<ans2) end=mid1; else st=mid2; } for(j=st;j<=end;j++) { double ans=dis(a,va1[j])+dis(a-b,va1[j]-va2[i])+l[i]; if(ans<min) { min=ans; lans=j; rans=i; } } } printf("%d %d\n",lans,rans); } return 0; }
代码2:
第一个代码交上去发现时间有点长...后来想到,既然已经枚举y2了,那么一定是在A,B,C三点共线的情况是最好的,所以利用斜率直接2分就可以了,OMG真是弱爆了,引用我们金工老师的话就是:“脑子都僵掉了”.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<algorithm> #include<cmath> using namespace std; #define maxn 110000 int va1[maxn],va2[maxn],l[maxn]; double dis(int x,int y) { return sqrt(1.0*x*x+1.0*y*y); } int main() { int n,m,a,b,lans,rans,i,j; double min=10000000000; while(scanf("%d%d%d%d",&n,&m,&a,&b)!=EOF) { for(i=1;i<=n;i++) { scanf("%d",&va1[i]); } for(i=1;i<=m;i++) { scanf("%d",&va2[i]); } for(i=1;i<=m;i++) { scanf("%d",&l[i]); double good=a*1.0/b*va2[i]; j=lower_bound(va1+1,va1+n+1,good)-va1; double ans=dis(a,va1[j])+dis(a-b,va1[j]-va2[i])+l[i]; if(ans<min) { min=ans;lans=j;rans=i; } if(j>=2) { double ans=dis(a,va1[j-1])+dis(a-b,va1[j-1]-va2[i])+l[i]; if(ans<min) { min=ans;lans=j-1;rans=i; } } } printf("%d %d\n",lans,rans); } return 0; }
题意:
有个人在一个n层的迷宫里走路,刚开始他在第n层的第一个位置,方向向右,然后开始走路,如果途中遇到前面有混凝土墙则转向,如果遇到石头墙,则先撞坏墙(墙变成空)然后再转向,如果在碰到障碍物前遇到脚下是空的情况,则直接进入下一层,问多长时间以后才能到达第一层.
赛时想法:
很不好的习惯,不按着顺序做就会觉得不自在,这题当时根本没看...也可以说是被那张图吓到了吧- -.没想到是道模拟..
正确思路:
其实就是按照题目的思路模拟,但是由于每行的长度m很大,不能正常的每次++或--,要记录左右边界,每次直接拓展左右边界,这样每行复杂度最多也就是10000.
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> char map[110][10010]; int main() { int n,m,dir,pos,l,r,i,c; __int64 ans; scanf("%d%d",&n,&m); ans=0; for(i=1;i<=n;i++) { scanf("%s",map[i]+1); map[i][0]=map[i][m+1]='#'; } dir=1,pos=1;//记录方向,dir=1代表向右,dir=0代表向左,pos代表当前的位置 for(i=1;i<n;i++) { c=0; l=r=pos;//左右边界一开始等于出发点,然后分别向左右拓展开 while(1) { if(dir) { while(map[i][r+1]=='.'&&map[i+1][r]!='.') r++; ans+=r-pos+1; pos=r; if(map[i+1][r]=='.') { break; } else if(map[i][r+1]=='+') { map[i][r+1]='.'; dir=!dir; c=0;//如果是石头墙,则上次碰到混凝土墙的标记应取消 } else if(!c&&map[i][r+1]=='#') { c=1;//如果碰到混凝土墙则标记 dir=!dir; } else { printf("Never\n");//碰到2次混凝土墙还没有跳出,说明不可能出去了. return 0;//由于CF是分组测试,可以直接retun跳出,免去不必要的标记. } } else { while(map[i][l-1]=='.'&&map[i+1][l]!='.') l--; ans+=pos-l+1; pos=l; if(map[i+1][l]=='.') { break; } else if(map[i][l-1]=='+') { map[i][l-1]='.'; dir=!dir; c=0; } else if(!c&&map[i][l-1]=='#') { c=1; dir=!dir; } else { printf("Never\n"); return 0; } } } } printf("%I64d\n",ans); return 0; }