问题描述
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;
}