csp模拟测试_1 T3 可怕的宇宙射线(dfs+剪枝)

问题描述

在这里插入图片描述

Input

在这里插入图片描述

Output

输出一个数ans,表示有多少个位置会被降智打击。

Sample Input

4
4 2 2 3

Sample Output

39

在这里插入图片描述
在这里插入图片描述

解题思路以及关键代码

全部代码

第一次代码,不够简洁和优化 并且没有进行剪枝 所以只能得到40分
(这个题n的数据最大达到了30,如果每次分裂的点都进行搜索的话,时间复杂度是O(2^n)肯定是过不去的)

#include<iostream>
using namespace std;
const int o = 500;
bool p[o*2][o*2]={0}; 
int a[30];
int n;
int ans=0;
void func(int x,int y,int count,int dir_x,int dir_y)
{
	if(count==n) return;    //dir_x dir_y记录上一次扩展的方向 
	int new_x=x;int new_y=y;
	int x1,x2,y1,y2;  		//本次要扩展的方向 
	if(dir_x==1&&dir_y==0)
	{
		x1=1;y1=1;x2=1;y2=-1;
	}
	else if(dir_x==0&&dir_y==1)
	{
		x1=1;y1=1;x2=-1;y2=1;
	}
	else if(dir_x==1&&dir_y==1)
	{
		x1=1;y1=0;x2=0;y2=1;
	}
	else if(dir_x==-1&&dir_y==-1)
	{
		x1=-1;y1=0;x2=0;y2=-1;
	}
	else if(dir_x==-1&&dir_y==0)
	{
		x1=-1;y1=1;x2=-1;y2=-1;
	}
	else if(dir_x==0&&dir_y==-1)
	{
		x1=-1;y1=-1;x2=1;y2=-1;
	}
	else if(dir_x==-1&&dir_y==1)
	{
		x1=0;y1=1;x2=-1;y2=0;
	}
	else if(dir_x==1&&dir_y==-1)
	{
		x1=1;y1=0;x2=0;y2=-1;
	}
	for(int y=0;y<a[count];y++)
	{
        new_x+=x1;new_y+=y1;
	    if(p[new_x][new_y]!=1)
		{
			p[new_x][new_y]=1;
			ans++;
		}	
	}
	if(count!=n)
	{
		func(new_x,new_y,count+1,x1,y1);	
	}
	new_x=x;new_y=y;
	for(int y=0;y<a[count];y++)
	{
	    new_x+=x2;new_y+=y2;
	    if(p[new_x][new_y]!=1)
		{
			p[new_x][new_y]=1;
			ans++;
		}
	}
	
	if(count!=n)
	{
		func(new_x,new_y,count+1,x2,y2);	
	}
	
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	int a_x=o,a_y=o;
	for(int i=0;i<a[0];i++)
	{
		ans++;
		p[a_x][a_y]=1;
		a_y++;
	}
	func(a_x,a_y,1,0,1);	
	cout<<ans;
	return 0;
 } 

改进版的代码 加了记忆性剪枝!即某个点已经分裂过并且下次分裂的方向被保存过,则不要再从这个点分裂了。因为如果现在的情况以前遇到过,那么再继续下去也和以前的经历完全一样。

代码说明

int dx[8]={ 0, 1, 1, 1, 0,-1,-1,-1};
int dy[8]={ 1, 1, 0,-1,-1,-1, 0, 1};//数组相邻的位置 就是方向相邻 

func(count+1,x,y,(direction+1)%8);
func(count+1,x,y,(direction+7)%8);

这里用一个常量数组以及%8,代替了第一次例子中冗余的调整方向问题。
常量数组排序就是按照一个时针顺序排序的,可以画一个坐标系依次取各象限以及坐标轴上的点就可以看出。

int vis[500][500][30][8];

vis[x][y][count][direction]=1;

每次递归都会保存一下曾经进行过的分裂。

#include<iostream>
using namespace std;
int a[31];
bool map[500][500]={0};
int n;
int ans=0;
int dx[8]={ 0, 1, 1, 1, 0,-1,-1,-1};
int dy[8]={ 1, 1, 0,-1,-1,-1, 0, 1};//数组相邻的位置 就是方向相邻 
int vis[500][500][30][8];
void func(int count,int x,int y,int direction) //direction表示当前要进行的方向 
{
	
	if(count>n||vis[x][y][count][direction])
	{
		return;
	}
	vis[x][y][count][direction]=1;
	for(int i=1;i<=a[count];i++)
	{
		x+=dx[direction];
		y+=dy[direction];
		if(!map[x][y])
		{
			map[x][y]=1;
			ans++;
		}
	}
	func(count+1,x,y,(direction+1)%8);
	func(count+1,x,y,(direction+7)%8);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	func(1,250,250,0);
	cout<<ans;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值