week4实验C题 可怕的宇宙射线 dfs

题目:
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图),初始方向默认向上。宇宙射线会在发射出一段距离后分裂,向该方向的左右45方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai个单位长度。
求宇宙射线会覆盖多少区域?
输入:
第一行包含一个正整数n(n <= 30),示宇宙射线会分裂n次
第二行包含n个正整数a1, a2…an.第i个数ai(ai <= 5)表示第i次分裂的宇宙射线会在它原方向上继续走多少个单位长度。
输出:
输出个数ans,表示有多少个位置会被覆盖。
样例输入:
4
4 2 2 3
样例输出:
39

在这里插入图片描述
对于这个问题,最关键的应该是要考虑到分裂的射线会在一些格子上重叠。在比赛的时候想到了要用递归,但是在递归的过程中如何处理各条分裂的边一直没什么头绪,而且如何计算所有被覆盖的点数也让我很头疼。赛后看了大佬的代码才知道要怎么做。
用set可以很好的解决重复点和点数的问题。
首先,我们不考虑其他分裂出的边,只考虑一条一直向右边转45°的边,在转了n次后,走到最后的点后,一边回溯,一边把当前的点插入set中,在到达转折点的时候,遍历一遍set,根据该转折的方向和转折点的坐标可以求出直线方程,可以利用高中的知识,求出set中的点关于这条直线的对称点,将对称点插入set,然后继续回溯,重复操作,直到最后。

关键函数solve

void solve(int dir,int k,pair<int,int> here)
{
 if(k>=n)
  return;
 pair<int,int> p=here;//用p保存转折点
 //利用偏移量方便地进行转折 
 offset[0].first=0;offset[0].second=1;//右转部分 
 offset[1].first=1;offset[1].second=1;
 offset[2].first=1;offset[2].second=0;
 offset[3].first=1;offset[3].second=-1;
 offset[4].first=0;offset[4].second=-1;
 offset[5].first=-1;offset[5].second=-1;
 offset[6].first=-1;offset[6].second=0;
 offset[7].first=-1;offset[7].second=1;
 
 for(int i=0;i<a[k];i++)//在指定方向上移动a[k]步 
 {
  here.first=here.first+offset[dir].first;
  here.second=here.second+offset[dir].second;
 } 
 
 solve((dir+1)%8,k+1,here);//递归 
 
 //回溯的同时将点插入set 
 for(int i=0;i<a[k];i++)
 {
  s.insert(here);
  here.first-=offset[dir].first;
  here.second-=offset[dir].second;
 }
 
 if(k!=0)//k=0的情况下就是最开始向上的那条边,不需要对称
 for(set<pair<int,int> >::iterator it=s.begin();it!=s.end();it++)
 {   //将点对称 
  pair<int,int> pos;
  if(dir==0||dir==4)
  {
   pos.first=p.first+p.second-it->second;
   pos.second=p.first+p.second-it->first;
  }
  else if(dir==1||dir==5)
  {
   pos.first=2*p.first-it->first;
   pos.second=it->second;
  }
  else if(dir==2||dir==6)
  {
   pos.first=it->second-p.second+p.first;
   pos.second=it->first+p.second-p.first;
  }
  else if(dir==3||dir==7)
  {
   pos.first=it->first;
   pos.second=2*p.second-it->second;
  }
  s.insert(pos);
 }
}

以下是全部代码:

#include<iostream>
#include<set>
using namespace std;
int a[100]; 
int n;

pair<int,int> offset[8];
set<pair<int,int> > s;
void solve(int dir,int k,pair<int,int> here)
{
 if(k>=n)
  return;
 pair<int,int> p=here;
 //利用偏移量方便地进行转折 
 offset[0].first=0;offset[0].second=1;//右转部分 
 offset[1].first=1;offset[1].second=1;
 offset[2].first=1;offset[2].second=0;
 offset[3].first=1;offset[3].second=-1;
 offset[4].first=0;offset[4].second=-1;
 offset[5].first=-1;offset[5].second=-1;
 offset[6].first=-1;offset[6].second=0;
 offset[7].first=-1;offset[7].second=1;
 
 for(int i=0;i<a[k];i++)//在指定方向上移动a[k]步 
 {
  here.first=here.first+offset[dir].first;
  here.second=here.second+offset[dir].second;
 } 
 
 solve((dir+1)%8,k+1,here);//递归 
 
 //回溯的同时将点插入set 
 for(int i=0;i<a[k];i++)
 {
  s.insert(here);
  here.first-=offset[dir].first;
  here.second-=offset[dir].second;
 }
 
 if(k!=0)
 for(set<pair<int,int> >::iterator it=s.begin();it!=s.end();it++)
 {//将点对称 
  pair<int,int> pos;
  if(dir==0||dir==4)
  {
   pos.first=p.first+p.second-it->second;
   pos.second=p.first+p.second-it->first;
  }
  else if(dir==1||dir==5)
  {
   pos.first=2*p.first-it->first;
   pos.second=it->second;
  }
  else if(dir==2||dir==6)
  {
   pos.first=it->second-p.second+p.first;
   pos.second=it->first+p.second-p.first;
  }
  else if(dir==3||dir==7)
  {
   pos.first=it->first;
   pos.second=2*p.second-it->second;
  }
  s.insert(pos);
 }
}
int main()
{
 cin>>n;
 for(int i=0;i<n;i++)
 {
  cin>>a[i];
 }
 pair<int,int> here;
 here.first=0;
 here.second=0;
 solve(0,0,here);
 cout<<s.size()<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值