Week4 CSP-M1 C - 可怕的宇宙射线

题目描述:

宇宙射线可以摧毁人的智商,进行降智打击!宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45°方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai个单位长度。计算共有多少个位置会被打击。

下面这个图,从上往下看

数据范围:

思维履历:

起初的思路很混乱,但是无外乎两个方向:一个是对称;一个是试着找公式,公式我觉得我如果能找到,还得修炼个几年

说说对称:之所以没写对称是因为遇到了如下几个问题(当时想着用数组存储这个图,有填充是1,没填充是0):

① 填充完一条线后,为了对称找新的点,怎么找出现有的已经被填充的所有点?很显然遍历数组不行

② 为了解决①,很显然,我可以不用模拟,而用点的结构体数组只存储当前已经被填充的点,然后坐标变换,得到新的点再加进来,但是出现了新的问题,就是在这个新的结构体数组中,有很多重复的点,怎么去重?为了解决这个问题,我发现了集合。

然后,集合真是个好东西,直接把点存集合里就完了

AC代码的基本思路:

第一次不算分裂只算前进,最后特殊处理;

从第二次分裂开始,每层递归都是如下过程:蔓延(分裂)出一条线,填充这条线(把这条线上的点放入集合),进入下一层(以当前这条线的末尾坐标为下一层的分裂点),找出集合的所有点,产生对称点,然后加入集合;

递归结束后,再加上第一次分裂的那条线,完毕。

代码:

① 纯DFS,40分,n>20时 TLE

 1 //暴力搜索
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <algorithm>
 6 using namespace std;
 7 bool m[5005][5005];
 8 int a[40];
 9 int n,ans=0;
10 //右,右下,下,左下,左,左上,上,右上 
11 //最初箭头指向dir=6,向上走 
12 int dx[]={0,+1,+1,+1,0,-1,-1,-1};
13 int dy[]={+1,+1,0,-1,-1,-1,0,+1};
14 void dfs(int dir,int num,int x,int y)   //从dir方向分裂,该分裂是第num次分裂,走a[num]步,分裂的点是x,y 
15 {
16     if(num>n) return;
17     //分裂的两个方向 
18     int l=(dir-1+8)%8;
19     int r=(dir+1)%8;
20     int new_x=x,new_y=y;
21     //向左分裂
22     for(int i=1;i<=a[num];i++)
23     {
24         new_x+=dx[l],new_y+=dy[l];
25         m[new_x][new_y]=1; 
26     }
27     dfs(l,num+1,new_x,new_y);
28     
29     //向右分裂
30     new_x=x,new_y=y;
31     for(int i=1;i<=a[num];i++)
32     {
33         new_x+=dx[r],new_y+=dy[r];
34         m[new_x][new_y]=1; 
35     }
36     dfs(r,num+1,new_x,new_y);
37 }
38 int main()
39 {
40     memset(m,0,sizeof(m));
41     cin>>n;
42     for(int i=1;i<=n;i++)
43         scanf("%d",a+i);
44     int x=2500;
45     for(int i=1;i<=a[1];i++)
46         m[x--][2500]=1;
47     
48     dfs(6,2,2500-a[1],2500);
49     
50     for(int i=0;i<5005;i++)
51         for(int j=0;j<5005;j++)
52             if(m[i][j]==1) ans++; 
53 
54     cout<<ans<<endl;
55     return 0;
56 } 
View Code

② 用对称和集合改进的代码

 1 /* 用set的好处:最后不必遍历整个数组; 可以存负数的边 ;*/
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <algorithm>
 6 #include <set>
 7 using namespace std;
 8 struct point
 9 {
10     int x,y;
11     bool operator< (const point &X) const
12     {
13         if(x!=X.x) return x<X.x;
14         else return y<X.y;
15     }
16 };
17 set <point> S;
18 int a[40];
19 int n,ans=0;
20 //右,右下,下,左下,左,左上,上,右上 
21 //最初dir=6,向上走 
22 int dx[]={ +1, +1,  0, -1, -1, -1,  0, +1};
23 int dy[]={  0, -1, -1, -1,  0, +1, +1, +1};
24 void dfs(int dir,int num,int x,int y)   //从dir方向分裂,该分裂是第num次分裂,走a[num]步,分裂的点是x,y 
25 {
26     if(num>n) return;
27     //分裂的方向 
28     int r=(dir+1)%8;
29 
30     //new_x和new_y是下一次分裂并且位移后
31     int new_x=x+dx[r]*a[num] , new_y=y+dy[r]*a[num];
32     dfs(r,num+1,new_x,new_y);
33     
34     //填充这一层
35     new_x=x,new_y=y;
36     for(int i=1;i<=a[num];i++)
37     {
38         new_x+=dx[r],new_y+=dy[r];
39         S.insert( {new_x,new_y} ); 
40     }
41     
42     set<point> St; 
43     for(auto t:S)   //遍历集合中的每一个点,生成对称点 
44     {
45         //判断这一层是通过哪个方向的分裂进入到下一层的
46         switch(r%4)
47         {
48             case 1: St.insert( { t.x,y-t.y+y } ); break;  //关于y=y对称  
49             
50             case 2: St.insert( { x+y-t.y,x+y-t.x } ); break;  //关于y=-x 对称 没问题 
51             
52             case 3: St.insert( { x+(x-t.x),t.y } ); break;   //关于x=x对称 
53             
54             case 0: St.insert( { x+t.y-y,y+t.x-x } ); break;  //关于y=x对称 
55         }
56     }
57     S.insert( St.begin(),St.end() );
58     
59 }
60 int main()
61 {
62 //    freopen("a.in","r",stdin); 
63     cin>>n;
64     for(int i=1;i<=n;i++)
65         scanf("%d",a+i);
66     
67     dfs(6,2,5000,5000);
68     for(int i=0;i<a[1];i++)
69     {
70         S.insert( {5000,5000-i} );
71      } 
72     cout<<S.size()<<endl;   //有可能起点也被填充了多次,所以 
73     return 0;
74 } 
View Code

总结:

比赛时也要冷静分析,问题抽象

需多多了解STL,能省很多事

两个问题:
1、对称直线的斜率是1,但方程不一定是y=x,因为不一定经过原点
2、对拍时,如果只拍了一组数据(输出相同)就停了,可能是代码中有 system("pause")等类似语句使程序不能直接结束
posted @ 2020-03-17 16:44  菜鸡今天学习了吗  阅读(196)  评论(0编辑  收藏  举报