week4 CSP M1 分裂的宇宙射线

题目描述

众所周知,瑞神已经达到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,在着一种叫做苟狗的生物,这种生物天生就能达到人类研究生的知识水平,天生擅长CSP,甚至有全国第一的水平! 但最可怕的是,它可以发出宇宙射线!宇宙射线可以摧毁人的智商,进行降智打击!
宇宙射线会在无限的二维平面上传播(可以看做一个二维网格图), 初始方向默认向上。宇宙射线会在发射出一段距离分裂,向该方向的左右45%方向分裂出两条宇宙射线,同时威力不变!宇宙射线会分裂n次,每次分裂后会在分裂方向前进ai个单位长度。
现在瑞神要带着他的小弟们挑战苟狗,但是瑞神不想让自己的智商降到普通本科生zjm那么菜的水平,所以瑞神来请求你帮他计算出共有多少个位置会被"降智打击”。
Input
输入第一行包含一个正整数n(n <= 30),表示宇宙射线会分裂n次。
第二行包含n个正整数a1, a2…an,第i个数ai(ai <= 5)表示第i次分裂的宇宙射线会在它原方向上继续走多少个单位长度。
Output
输出一个数ans,示有多少个位置会被降智打击。

样例

Input
4
4 2 2 3
Output
39

在这里插入图片描述

思路分析

这题最直接的想法肯定从起点开始进行,把每个到达的点都做个标记,求出不重复的点的个数,所以我用dfs进行搜索,这种方法的复杂度必然很高。

初始Dfs过程:当前点的横纵坐标为(a,b)分裂次数num,前进方向d。

1.若num等于最大分裂次数,回溯。

2.根据num确定前进长度,每次往前进方向走一个单位,并判断点是否到达。

3.-45°进行dfs。

4.+45°进行dfs。

用0~7来对应8个方向,用数组dx[],dy[]来计算坐标变化。(一开始还用字符串表示,map映射,分别写了八个函数什么的,太傻了)
-45°:(d+1)%8;
+45°:(d+7)%8;

在这里插入图片描述

所以我们进行合理的剪枝。首先我想到的就是同一个点往同一个方向,并且前进长度相同时就可以剪掉,并且用了几个例子感觉都对,但一交就是WA,这种剪枝存在的问题是,就算同一个方向、前进长度相同,它的下一次分裂也会前进不同的长度。所以要考虑到分裂次数,同一个点往同一个方向,并且在同一次分裂时就可以剪掉,也不会影响后续的分裂。(其实这种一开始想到了,但觉得前一种可以剪掉更多的情况,却没有考虑到对后续分裂的影响)。

用二维数组reach来标记是否到达,四维数组divide来标记分裂情况。在dfs过程中,若当前点对应四维数组中元素为1,则回溯。

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
/*
struct point
{
 int a,b;
 point(){}
 point(int x,int y)
 {
  a=x;b=y;
 }
 bool operator<(const point &p) const
 {
  if(a!=p.a) return a<p.a;
  else
      return b<p.b;
 }
};
struct point1
{
 int a,b;
 int num;//分裂次数 
 int d;//分裂方向 
 point1(){}
 point1(int x,int y,int Num,int D)
 {
  a=x;b=y;num=Num;d=D;
 }
 bool operator<(const point1 &p1) const
 {
  if(a!=p1.a) return a<p1.a;
  else
  {
   if(b!=p1.b) return b<p1.b;
   else
   {
    if(num!=p1.num) return num<p1.num;
    else
    {
     if(d!=p1.d) return d<p1.d;
    } 
   }
  }
 }
};*/
int n,ans=0,count[30];
int dx[8]={0,1,1,1,0,-1,-1,-1};
int dy[8]={1,1,0,-1,-1,-1,0,1};
//map<point,bool> reach;//是否到达 
//map<point1,bool> divide;//分裂情况 
int reach[400][400]={0}; 
int divide[400][400][5][8]={0};
int tag=0;
void dfs(int a,int b,int num,int d) 
{
 if(num==n) return;//停止分裂
 if(divide[a][b][num][d]==1) return; 
 //在此次分裂中已经向同方向分裂过了不必再分裂了
 divide[a][b][num][d]=1;
 for(int i=0;i<count[num];i++)
    {
     a+=dx[d];
     b+=dy[d];
     if(reach[a][b]==0)
     {
   reach[a][b]=1;
      ans++;
   //printf("%d %d %d %d\n",p.a,p.b,num,d);
  }
 }
 dfs(a,b,num+1,(d+7)%8);//-45 
 dfs(a,b,num+1,(d+1)%8);//+45 
}
int main()
{
 scanf("%d",&n);
 for(int i=0;i<n;i++)
 {
  scanf("%d",&count[i]);
 }
 dfs(200,200,0,0);
 printf("%d %d",ans,tag);
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rwyoi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值