题目链接:点击打开链接
N - 简单的双向搜索
Time Limit: 2000/1000MS (Java/Others) Memory Limit: 131072/131072KB (Java/Others)
给定三个大小为 N 的整型数组 A1,A2,A3 ,和三个整数 B1,B2,B3 。接下来依次做 N 次操作:对于第 i 次操作 (i=1,2,..,N) ,选择一个数 j ( j=1,2,3 ),令 Bj 加上 Aj[i] 。问是否存在一种操作序列,使得最后 B1=B2=B3=0 。
Input
第一行四个数 N(1<=N<=25),B1,B2,B3 。接下来N行,每行三个整数,表示 A1[i],A2[i] 和 A3[i] 。保证 |A1[i]|,|A2[i]|,|A3[i]|,|B1|,|B2|,|B3|<=109 。
Output
如果存在符合要求的方案,输出 "YES" ;否则输出 "NO" 。
这是双向搜索的入门题,也是经典的选数问题,双向bfs相对于单向bfs无论是在时间还是空间复杂度上都优于单向bfs,bfs实际上任然是一种盲目搜索算法,它考虑完一个层次后,才会考虑下一个层次,所以状态数成指数级增长,对空间消耗非常大,如果已知目标状态,我们考虑使用双向bfs,空间消耗在指数上大约能减少一半;对于这道题,由于这不是一道求最短路的问题,而是一道判断题,所以我们不需要记忆化搜索,我们先正向走n/2步,此时,用map保存,反向走n-n/2步,查此时的状态是否在map中出现过。
#include<bits/stdc++.h>
using namespace std;
int a[3][30];
struct node1{
int b[3];
int step;
};
vector<int>v;
map<vector<int>,int>m3;
queue<node1>q1;
queue<node1>q2;
int main()
{
int b[3],n,i,j,k;
scanf("%d %d %d %d",&n,&b[0],&b[1],&b[2]);
for(i=0;i<n;i++)
for(j=0;j<3;j++)
scanf("%d",&a[j][i]);
struct node1 sta,des;
for(i=0;i<3;i++)
sta.b[i]=b[i];
sta.step=0;
for(j=0;j<3;j++)
des.b[j]=0;
des.step=0;
q1.push(sta);
q2.push(des);
struct node1 x,y;
v.resize(3);
while(1)
{
if(!q1.empty())//正向走n/2步
{
x=q1.front();
if(x.step==n/2)
break;
q1.pop();
for(i=0;i<3&&x.step<n;i++)
{
y=x;
y.b[i]+=a[i][y.step];
y.step++;
if(y.step==n/2)//存入容器中
{
v[0]=y.b[0];
v[1]=y.b[1];
v[2]=y.b[2];
m3[v]=1;
continue;
}
q1.push(y);
}
}
else
break;
}
while(1)
{
if(!q2.empty())//反向走n-n/2步
{
x=q2.front();
if(x.step>n-n/2)
{
printf("NO");
return 0;
}
q2.pop();
for(i=0;i<3&&x.step<n;i++)
{
y=x;
y.b[i]-=a[i][n-y.step-1];
y.step++;
if(y.step==n-n/2)//比较
{
v[0]=y.b[0];
v[1]=y.b[1];
v[2]=y.b[2];
if(m3.count(v))
{
printf("YES");
return 0;
}
continue;
}
q2.push(y);
}
}
else
{
printf("NO");
return 0;
}
}
return 0;
}