T3 光
提前声明:递归层数太多会炸的所以改成while1,return改成break或continue...以下写的return请自动视为break,抱歉。。。。。。
大模拟(很恶心的模拟题),细节巨多。。。
因为这是道细节题而且做了将近10个小时,,,所以改完赶快来写一下...
60分暴力很好打当然是减少码量的前提下。
没有点性质我感觉无从下手,打的时候情况巨多,打了四个大if后就不想打了,,,然后开始找性质:
既然是模拟那么需要知道
1.方向 2.障碍 3.遇到障碍后的变向 4.什么时候跳出。
主要细节:方向!!!题目的方向不是地理上的..............................
为了防止大码量用tx,ty表示方向在模拟没有遇到障碍时就可以直接对x,y+-操作。
那遇到障碍了呢? ——>特判?NO!为了防止大码量需要用到一个性质:
我们遇到障碍的情况有一块两块三块。 所以一块两块三块还是要判的别怕三个if就5行。
主要恶心的时从那个方向遇到的障碍,手玩一下会开心的发现在这三块中有一块是按当前方向走的而另两块一块
于当前的x同,另一块与当前的y同。我们就可以卡去一堆冗杂代码。。
什么时候跳出:遇到三个的死角可以有两种做法一种是记录一下然后反向走,最后ans除二(从skyh那颓的),一种是直接return,那么就可能有一些点没计入,那就再反向走一遍(从wc_LH处颓)。非死角第一种判回到起点,第二种判到过这个点就return.
暴力细节:需要注意如果直接死角处return,遇到已经搜过的点就return的话会有遗漏所以要反向(q)搜
因为边界要反弹,直接预处理成黑点。
30行暴力较简短。。。。
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 char ch[2]; 5 int x,y,tx,ty,ans; 6 int n,m,k,scc[1005],cde[1005]; 7 bool judge[1005][1005],mark[1005][1005]; 8 void search(int x,int y,int tx,int ty){ 9 if(judge[x][y]) return; 10 ans++; judge[x][y]=1; 11 if(!mark[x+tx][y+ty]) search(x+tx,y+ty,tx,ty); 12 else 13 if(mark[x][y+ty]&&mark[x+tx][y]||!mark[x][y+ty]&&!mark[x+tx][y]) return; 14 else if(mark[x][y+ty]) search(x+tx,y,tx,-ty); 15 else if(mark[x+tx][y]) search(x,y+ty,-tx,ty); 16 } 17 int main() 18 { 19 scanf("%d%d%d",&n,&m,&k); 20 for(int i=1;i<=k;i++){ 21 scanf("%d%d",&scc[i],&cde[i]); 22 mark[scc[i]][cde[i]]=1; 23 } 24 scanf("%d%d%s",&x,&y,ch); 25 tx= ch[0]=='N'?-1:1; ty= ch[1]=='W'?-1:1; 26 for(int i=0;i<=n+1;i++) mark[i][0]=mark[i][m+1]=1; 27 for(int i=0;i<=m+1;i++) mark[0][i]=mark[n+1][i]=1; 28 search(x,y,tx,ty); search(x-tx,y-ty,-tx,-ty); 29 printf("%d",ans); 30 }
正解:暴力开1000×1000数组正解显然不能,而且数据如此之大,暴枚每一步会死的很惨。。。
优化:发现每次其实只有障碍物处有用,其间一定是一条直线,ans直接加上坐标差。
如何查找下一次的障碍物坐标呢,开数组?RE! 用set/vector. 本人推荐用set自动排序去重也自带lower和upper操作一个强大的C++ STL容器。
需要用到两条性质证明见Deepinc大佬题解
对每个x+y,x-y开一个set,插入x并以此排序(set自排)因为每条直线都唯一对应,正符合上面的直接查找障碍物。 细节1:x-y有负加上一个固定大值;
每个点只会被一种方向的光线扫过(东北<->西南,西北<->东南);
方向和障碍物及障碍物跳转与上面基本一支,但障碍物的查询提一下,先在现在的x+y或x-y中找到下一个方向上的黑点。根据上面暴力中的那条性质:
让障碍处的上一个位置的x/y不变可以得知另外两个块的坐标用其横坐标在其x+y中查询它如果结果等于它说明它是障碍,以此得知遇到障碍物的情况。
细节2:set的lower upper要用*号取出其存的值,在tx是-1时要找前驱所以找lower再--再补上*号;
这时考虑什么时候跳出:用上面暴力提到的第二种的话很复杂而且细节巨多,我写的程序已被WC卡掉,他用的是正反都搜一遍。
所以改成第一种。
这时一大波细节来了:首先不能用回到起点return因为枚举的是障碍所以可能把它跳过去然后死掉,找顺着开始方向的障碍点,把遇到这个障碍点且是这个方向作为return条件。 再者找着这个点后即使已经可以有判断return的条件也得注意不能从起点枚举,而要从障碍点的前一个位置开始,如果从起点枚举会发现ans少了一些,这是因为并不知道这个位置的位置在哪(你要是记录了再想一想加点特判什么的没准也行),再回来的时候判障碍加上这一段或者不加就会有一些点少加多加的问题......所以得从障碍的上一个位置干,我们在回来的时候直接加上这一段(从当前障碍点到起始障碍点)的长度-1(-1是因为其前一个位置已经算过了) (要加上再return这个细节是因为从开始向反方向找到的障碍这到正方向的障碍前一个位置这一段是只在回来的时候算了一遍u所以再加上一遍)。
记录有没有遇到死角直接弹回去这样会算两遍,ans/2;
第7个点有点恶心注意就是上面说的细节少算多算的送个小点2 2 0 1 1 SW
用两个方向的时候判断条件写成回到这条直线的时候及死角的时候return会得到90分的好成绩,第7个点WA,然后我换了第一种的正解,没从障碍的前一个位置搜而是从起点搜,然后回到障碍时直接return了而没有加上这一段,于是出来了和那个90分WA程序第7个点惊人一样的b数 8865393708还和答案长挺像现在都不知道为啥.................
说的有点乱,谅解谅解,多手玩多思考一些就行。
用到上面将暴力时的性质将4个大if干掉,代码量还是比较优秀1.8K.稍清秀。
1 #include<set> 2 #include<cmath> 3 #include<cstdio> 4 #include<iostream> 5 using namespace std; 6 #define t_d 100005 7 #define ll long long 8 set<int> tuba[200005],tubc[200010]; 9 char ch[2]; 10 ll n,m; 11 bool judge; 12 int k,remx,remy,num; 13 int tx,ty,nx,ny,fx,fy,sx,sy,rx,ry,rtx,rty; 14 long long ans,answ; 15 inline void search(int x,int y,int tx,int ty,int j) 16 { 17 while(1) 18 { 19 if(!(tx+ty)) 20 { 21 if(tx<0) nx=*(--tuba[x+y].lower_bound(x)) , ny=x+y-nx; 22 else nx=*tuba[x+y].upper_bound(x) , ny=x+y-nx; 23 } 24 if(!(tx-ty)) 25 { 26 if(tx<0) nx=*(--tubc[x-y+t_d].lower_bound(x)) , ny=nx-x+y; 27 else nx=*tubc[x-y+t_d].upper_bound(x) , ny=nx-x+y; 28 } 29 fx=*tuba[nx+ny-tx].lower_bound(nx-tx); 30 sx=*tuba[nx+ny-ty].lower_bound(nx); 31 if(j||(nx==rtx&&ny==rty&&tx==rx&&ty==ry&&ans)) 32 { 33 if(!j) ans+=abs(nx-x)-1; 34 break; 35 } 36 ans+=abs(nx-x); 37 if((fx==nx-tx&&sx==nx) || fx!=nx-tx&&sx!=nx ) 38 { 39 judge=1; 40 x=nx-tx,y=ny-ty,tx=-tx,ty=-ty; 41 continue; 42 } 43 if( fx==nx-tx )//x同射入方向,新x同nx 44 { 45 x=nx,y=ny-ty,tx=tx,ty=-ty;//search(nx,ny-ty,tx,-ty); 46 continue; 47 } 48 if( sx==nx ) x=nx-tx,y=ny,tx=-tx,ty=ty;//search(nx-tx,ny,-tx,ty); 49 } 50 } 51 int main() 52 { 53 int x,y; 54 scanf("%lld%lld%d",&n,&m,&k); 55 for(int i=1;i<=k;i++) 56 { 57 scanf("%d%d",&x,&y); 58 tuba[x+y].insert(x); 59 tubc[x-y+t_d].insert(x); 60 } 61 scanf("%d%d%s",&remx,&remy,ch); 62 for(int i=0;i<=n+1;i++) 63 { 64 tuba[i+0].insert(i); 65 tubc[i-0+t_d].insert(i); 66 tuba[i+m+1].insert(i); 67 tubc[i-m-1+t_d].insert(i); 68 } 69 for(int i=0;i<=m+1;i++) 70 { 71 tuba[0+i].insert(0); 72 tubc[0-i+t_d].insert(0); 73 tuba[n+1+i].insert(n+1); 74 tubc[n+1-i+t_d].insert(n+1); 75 } 76 tx= ch[0]=='N'?-1:1; ty= ch[1]=='W'?-1:1; 77 search(remx,remy,tx,ty,1); 78 rtx=nx; rty=ny; rx=tx; ry=ty; 79 search(nx-tx,ny-ty,tx,ty,0); 80 if(judge) ans/=2; 81 printf("%lld",ans); 82 }
总结:1.细节题一定要想清每一个点,否则调死也想不到哪的细节问题........
2.看题要看清题意一定要明了,考试的时候还以为任意方向射还写了个一次函数double愉快WA 0;T_T
3.懒就多动脑,有时候实在想不到也不能逃避码就码呗;
4.模拟题其实就是考细节所有情况都考虑就可以了,实在不行大暴力模拟。
5.找性质,有些东西会发现人眼一下子就能判断而想让电脑有双明亮的眼睛,其实这时候一般来说都是有些性质的,找到就会很清爽。
6.千万别yy,比如方向。。。题目一定要看清。。。