目录
J题:海贼王之伟大航路-OpenJ_Bailian - 4124
专题链接:
HNCU寒假训练专题一: 简单搜索 - Virtual Judge (vjudge.net)
A题:Dungeon Master-poj2251
题意:一个三维迷宫,需要从起点走到终点
想法:是一个三维搜索题,第一次遇到三维的,不过和二维其实没有太大区别。
就是二维的因为只有两个变量,所以可以用pair来存,但是三维的好像就只能用结构体。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
typedef struct
{
int x,y,z;
}point;
int l,r,c;
int sx,sy,sz;//存起始坐标
int ex,ey,ez;//存终点坐标
const int N=35;
char g[N][N][N];
bool vis[N][N][N];
int step[N][N][N];
int dir[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
void bfs()
{
queue<point>q;
point now;
now.x=sx,now.y=sy,now.z=sz;
step[sx][sy][sz]=0;
q.push(now);
vis[sx][sy][sz]=true;
while(!q.empty())
{
int x=q.front().x;
int y=q.front().y;
int z=q.front().z;
if(x==ex&&y==ey&&z==ez)
{
printf("Escaped in %d minute(s).\n",step[ex][ey][ez]);
return;
}
q.pop();
for(int i=0;i<6;++i)
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
int zz=z+dir[i][2];
if(xx>=0&&xx<l&&yy>=0&&yy<r&&zz>=0&&zz<c&&!vis[xx][yy][zz]&&g[xx][yy][zz]!='#')
{
now.x=xx,now.y=yy,now.z=zz;
q.push(now);
vis[xx][yy][zz]=true;
step[xx][yy][zz]=step[x][y][z]+1;
}
}
}
cout<<"Trapped!"<<endl;
}
int main()
{
while(cin>>l>>r>>c,l,r,c)
{
memset(vis,false,sizeof(vis));
for(int i=0;i<l;++i)
for(int j=0;j<r;++j)
for(int k=0;k<c;++k)
{
cin>>g[i][j][k];
if(g[i][j][k]=='S')
sx=i,sy=j,sz=k;
else if(g[i][j][k]=='E')
ex=i,ey=j,ez=k;
}
bfs();
}
return 0;
}
B题:Catch That Cow-poj3278
题意:给出两个坐标,一个是农夫的坐标,一个是奶牛的坐标。农夫只能往三个方向走,一个是x-1,一个是x+1,或者2*x,求农夫追到奶牛的最小步数。
想法:和普通广搜没什么区别,就是方向不一样,以及这是一维的搜索。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=100010;
int n,k,ans;
int step[N];
bool vis[N];
int bfs()
{
queue<int>q;
q.push(n);
step[n]=0;
vis[n]=true;
while(!q.empty())
{
int x=q.front();
q.pop();
if(x==k)
return step[k];
for(int i=0;i<3;++i)
{
int next;
if(i==0)
next=x+1;
else if(i==1)
next=x-1;
else if(i==2)
next=2*x;
if(next>=0&&next<=N&&!vis[next])
{
step[next]=step[x]+1;
q.push(next);
vis[next]=true;
}
}
}
}
int main()
{
cin>>n>>k;
ans=0;
cout<<bfs()<<endl;
}
C题:Fliptile-poj3279
题意:给出一个瓷砖矩阵,每翻转一次就会使上下左右及中间的一起翻动,求使所有瓷砖都为白色瓷砖的最小翻转次数
想法:参考Fliptile POJ - 3279(超详解)_Bright的博客-CSDN博客_poj3279
#include<iostream>
#include<cstring>
using namespace std;
const int N=17;
const int inf=20*20;
int dir[5][2]={{0,0},{0,1},{1,0},{-1,0},{0,-1}};//因为当前瓷砖的翻转受自己的翻转次数和周围四个瓷砖的翻转次数影响,所以五个方向
int g[N][N];
int num[N][N];//存储当前翻转次数
int ans[N][N];//存储最终输出的翻转次数
int res,cnt;//res存储最小翻转次数 cnt存储当前翻转次数
int m,n;
int getColor(int x,int y)//通过turn[ ]判断当前瓷砖的眼色
{
int temp=g[x][y];
for(int i=0;i<5;++i)
{
int dx=x+dir[i][0];
int dy=y+dir[i][1];
if(dx>=0&&dx<n&&dy>=0&&dy<m)
temp+=num[dx][dy];//如果是黑色就会翻转一次
}
return temp%2;//翻转两次就等于没翻转 所以除二取余
}
void dfs()
{
for(int i=1;i<n;++i)
for(int j=0;j<m;++j)
{
if(getColor(i-1,j))
{
num[i][j]=1;
cnt++;
}
if(cnt>res)//剪枝 如果当前次数已经大于最小次数 那就没必要继续进行下面的搜索了
return ;
}
//检测最后一层是否都是白色
for(int i=0;i<m;++i)
if(getColor(n-1,i))
return;
//如果当前次数更小,就更新最小次数以及ans数组
if(cnt<res)
{
memcpy(ans,num,sizeof num );
res=cnt;
}
}
int main()
{
cin>>n>>m;
res=inf;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
cin>>g[i][j];
//用二进制枚举第一行翻转的所有情况
for(int i=0;i<1<<m;++i)
{
cnt=0;
memset(num,0,sizeof num);
for(int j=0;j<m;++j)
{
num[0][m-1-j]=i>>j&1;//从后往前填才能让字典序尽量小
if(num[0][m-1-j])
cnt++;
}
dfs();
}
if(res==inf)
puts("IMPOSSIBLE");
else
{
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
cout<<ans[i][j]<<' ';
cout<<endl;
}
}
}
D题:Find The Multiple-poj1426
题意:给定一个正整数 n,编写一个程序来找出 n 的非零倍数 m,其十进制表示形式仅包含数字 0 和 1。
想法:这题我自己一开始是没想到的,从1开始不断深搜m*10和m*10+1就能遍历到所有的1和0组成的数字的情况。
#include<iostream>
using namespace std;
long long n,m;
bool flag;
void dfs(long long m)
{
if(flag||m<0)
return;
if(m%n==0)
{
cout<<m<<endl;
flag=true;
return;
}
else
{
dfs(m*10);
dfs(m*10+1);
}
}
int main()
{
while(cin>>n,n)
{
flag=false;
dfs(1);
}
}
E题:Prime Path-poj3126
题意:给出两个素数,要把第一个素数变成第二个素数,每次只能改变一位数,求最少变换次数。题目保证是四位数。
想法:转换为广搜。建个队列,每次出队依次遍历个十百千位,如果没有访问过,并且是素数就可以入队。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
bool isprime[10000];
bool vis[10000];
int step[10000];
int a,b;
bool is_prime(int x)//判断是否为素数
{
for(int i=2;i*i<=x;++i)
if(x%i==0)
return false;
return true;
}
void bfs()
{
queue<int>q;
q.push(a);
vis[a]=true;
step[a]=0;
while(!q.empty())
{
int x=q.front();
q.pop();
if(x==b)
{
cout<<step[x]<<endl;
return;
}
//改变个位
for(int i=1;i<=9;i+=2)
{
int y=x/10*10+i;
// cout<<y<<endl;
// getchar();
if(vis[y]||!is_prime(y))
continue;
q.push(y);
step[y]=step[x]+1;
vis[y]=true;
}
//改变十位
for(int i=0;i<=9;++i)
{
int y=x-10*(x%100/10)+10*i;
// cout<<y<<endl;
// getchar();
if(vis[y]||!is_prime(y))
continue;
q.push(y);
step[y]=step[x]+1;
vis[y]=true;
}
//改变百位
for(int i=0;i<=9;++i)
{
int y=x-100*(x/100%10)+100*i;
// cout<<y<<endl;
// getchar();
if(vis[y]||!is_prime(y))
continue;
q.push(y);
step[y]=step[x]+1;
vis[y]=true;
}
//改变千位
for(int i=1;i<=9;++i)
{
int y=x%1000+1000*i;
// cout<<y<<endl;
// getchar();
if(vis[y]||!is_prime(y))
continue;
q.push(y);
step[y]=step[x]+1;
vis[y]=true;
}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>a>>b;
memset(vis,false,sizeof(vis));
bfs();
}
}
H题:Fire!-UVA11624
这题也真的卡了好久 特别值得注意的是这里的起火点不止一个位置
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int>PII;
const int N=1010;
char g[N][N];
bool vis[N][N];
int step[N][N];
int dir[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
int x1,x2,y1,y2;
int n,m;
int size1;//人的逃跑路线大小
int size2;//火的大小
queue<PII>q2;//火灾的蔓延
void bfs()
{
queue<PII>q1;//人的逃跑
q1.push({x1,y1});
vis[x1][y1]=true;
step[x1][y1]=0;
while(!q1.empty())
{
int xx=size1;
size1=0;
while(xx--)
{
PII now=q1.front();
int x=now.first;
int y=now.second;
q1.pop();
if(g[x][y]=='F')
continue;
if(x==0||y==0||x==n-1||y==m-1)
{
cout<<step[x][y]+1<<endl;
return;
}
for(int i=0;i<4;++i)
{
int dx=x+dir[i][0];
int dy=y+dir[i][1];
if(dx<0||dx==n||dy<0||dy==m||g[dx][dy]!='.'||vis[dx][dy])
continue;
step[dx][dy]=step[x][y]+1;
vis[dx][dy]=true;
q1.push({dx,dy});
size1++;
}
}
xx=size2;
size2=0;
while(xx--)
{
PII cur=q2.front();
q2.pop();
for(int i=0;i<4;++i)
{
int dx=cur.first+dir[i][0];
int dy=cur.second+dir[i][1];
if(dx>=0&&dy>=0&&dx<n&&dy<m)
if(g[dx][dy]=='.')
{
g[dx][dy]='F';
size2++;
q2.push({dx,dy});
}
}
}
}
cout<<"IMPOSSIBLE"<<endl;
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
memset(vis,false,sizeof(vis));
while(!q2.empty())
q2.pop();
size1=1;
size2=0;
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
cin>>g[i][j];
if(g[i][j]=='J')
x1=i,y1=j;
else if(g[i][j]=='F')
{
size2++;
q2.push({i,j});
}
}
}
bfs();
}
}
I题:马走日-OpenJ_Bailian - 4123
广搜模板题
#include<iostream>
#include<cstring>
using namespace std;
int T;
int n,m,x,y;
int dir[8][2]={{-1,-2},{-2,-1},{-2,1},{1,2},{2,1},{1,-2},{-1,2},{2,-1}};
bool vis[15][15];
int ans;
int dfs(int x,int y,int step)
{
vis[x][y]=true;
if(step==n*m)
ans++;
for(int i=0;i<8;++i)
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(xx<n&&xx>=0&&yy<m&&yy>=0&&!vis[xx][yy])
{
vis[xx][yy]=true;
dfs(xx,yy,step+1);
vis[xx][yy]=false;
}
}
}
int main()
{
cin>>T;
while(T--)
{
cin>>n>>m>>x>>y;
memset(vis,false,sizeof(vis));
ans=0;
dfs(x,y,1);
cout<<ans<<endl;
}
}
J题:海贼王之伟大航路-OpenJ_Bailian - 4124
题意:要经过所有岛屿,且只能经过一次,求最短时间
想法:这好像是叫什么哈密顿路径,状压dp
#include<iostream>
#include<cstring>
using namespace std;
const int N=18;
int f[1<<N][N];//第一维是用二进制表示走过的路径 第二维是表示当前走到了哪里
int g[N][N];
int main()
{
int n;
while(cin>>n)
{
memset(f,0x3f,sizeof f);
for(int i=0;i<n;++i)//从0开始是为了方便用二进制表示
for(int j=0;j<n;++j)
cin>>g[i][j];
f[1][0]=0;//初始化起点 0001
for(int i=1;i<1<<n;++i)
for(int j=0;j<n;++j)
if(i>>j&1)//如果i这种状态经过了j这个点
{
for(int k=0;k<n;++k)
if(i>>k&1)//如果i也经过了k这个点
f[i][j]=min(f[i][j],f[i-(1<<j)][k]+g[k][j]);
}
cout<<f[(1<<n)-1][n-1]<<endl;
}
}
K题:迷宫问题-OpenJ_Bailian - 4127
题意:给出一个迷宫矩阵,输出最短路径
想法:也是简单题吧,就是要记录最短路径有点麻烦
#include<iostream>
#include<queue>
#include<cstring>
#include<stack>
using namespace std;
typedef pair<int, int> PII;
int g[7][7];
PII pre[7][7];
PII path[50];
bool vis[7][7];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
void bfs()
{
queue<PII>q;
q.push(make_pair(0,0));
vis[0][0]=true;
while(!q.empty())
{
int x=q.front().first;
int y=q.front().second;
if(x==4&&y==4)
break;
q.pop();
for(int i=0;i<4;++i)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(tx<0||tx>=5||ty<0||ty>=5||g[tx][ty]==1||vis[tx][ty])
continue;
pre[tx][ty]={x,y};
q.push(make_pair(tx,ty));
vis[tx][ty]=true;
}
}
int x=4,y=4,k=0;
while(!(x==0&&y==0))
{
path[k++]={x,y};
// cout<<path[k-1].first<<' '<<path[k-1].second<<endl;
// cout<<"("<<x<<", "<<y<<")"<<endl;
int u=pre[x][y].first;//这里要特别注意另外设两个变量,不能直接用x和y
int v=pre[x][y].second;
x=u,y=v;
// cout<<"("<<x<<", "<<y<<")"<<endl<<endl;
}
// cout<<pre[1][4].first<<' '<<pre[1][4].second<<endl;
path[k++]={0,0};
for(int i=k-1;i>=0;--i)
cout<<"("<<path[i].first<<", "<<path[i].second<<")"<<endl;
}
int main()
{
for(int i=0;i<5;++i)
for(int j=0;j<5;++j)
cin>>g[i][j];
bfs();
return 0;
}
L题:鸣人和佐助-OpenJ_Bailian - 4115
#include<iostream>
#include<queue>
using namespace std;
struct node
{
int x,y;
int step;
int ckl;//查克拉数
node(int a,int b,int c,int d):x(a),y(b),step(c),ckl(d){ }//这样子写,赋值的时候方便一点
};
const int N=210;
int n,m,t;
char g[N][N];
bool vis[N][N][15];
int dir[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
int sx,sy;
int ans;
bool bfs()
{
queue<node>q;
// cout<<sx<<' '<<sy<<endl;
q.push(node(sx,sy,0,t));//这里就不可以直接node now了..
// cout<<q.front().x<<' '<<q.front().y<<endl;
vis[sx][sy][t]=true;
while(!q.empty())
{
node now=q.front();
q.pop();
int x=now.x;
int y=now.y;
// cout<<endl<<x<<' '<<y<<endl;
for(int i=0;i<4;++i)
{
int dx=x+dir[i][0];
int dy=y+dir[i][1];
if(dx<0||dx>=n||dy<0||dy>=m||vis[dx][dy][now.ckl])
continue;
if(g[dx][dy]=='+')
{
ans=now.step+1;
return true;
}
else if(g[dx][dy]=='#'&&now.ckl>0)
{
vis[dx][dy][now.ckl-1]=true;
q.push(node(dx,dy,now.step+1,now.ckl-1));
// cout<<dx<<' '<<dy<<endl;
}
else if(g[dx][dy]=='*')
{
vis[dx][dy][now.ckl]=true;
q.push(node(dx,dy,now.step+1,now.ckl));
// cout<<dx<<" "<<dy<<endl;
}
}
}
return false;
}
int main()
{
cin>>n>>m>>t;
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
cin>>g[i][j];
if(g[i][j]=='@')
sx=i,sy=j;
}
}
if(!bfs())
cout<<-1<<endl;
else
cout<<ans<<endl;
}
M题:红与黑-OpenJ_Bailian - 2816
题意:求站在当前位子上,最多能走多少块黑瓷砖,只能往上下左右四个方向走
想法:是一道做过无数的题目了,没什么好讲的了
#include<iostream>
#include<cstring>
using namespace std;
char g[25][25];
int m,n;
int mm,nn;
int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}};
bool vis[25][25];
int ans=0;
void dfs(int x,int y)
{
vis[x][y]=true;
ans++;
for(int i=0;i<4;++i)
{
int u=x+dir[i][0];
int v=y+dir[i][1];
if(g[u][v]=='#'||u<1||u>n||v<1||v>m||vis[u][v]==true)
continue;
dfs(u,v);
}
}
int main()
{
while(cin>>m>>n,n,m)
{
memset(vis,false,sizeof(vis));
ans=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
cin>>g[i][j];
if(g[i][j]=='@')
mm=i,nn=j;//记录当前所在位置
}
getchar();
}
dfs(mm,nn);
cout<<ans<<endl;
}
}
N题:棋盘问题-OpenJ_Bailian - 1321
题意:给定一个不规则棋盘,同行同列不能有两个棋子,求摆放k个棋子的所有可行方案数
想法:深搜,每次深搜传参是当前行数以及当前已经摆放了几个棋子
#include<iostream>
#include<cstring>
using namespace std;
int n,k,ans;
bool vis[10];
char g[10][10];
void dfs(int u,int k)
{
if(k==0)
{
ans++;
return;
}
for(int i=u;i<n;++i)
{
for(int j=0;j<n;++j)
{
if(vis[j]==true||g[i][j]=='.')
continue;
vis[j]=true;
dfs(i+1,k-1);
vis[j]=false;
}
}
}
int main()
{
while(cin>>n>>k)
{
if(n==-1&&k==-1)
break;
ans=0;
memset(vis,false,sizeof(vis));
for(int i=0;i<n;++i)
cin>>g[i];
dfs(0,k);
cout<<ans<<endl;
}
return 0;
}
O题:拯救行动-OpenJ_Bailian - 4116
题意:骑士救公主,移动一次需要一个时间,有守卫在的地方,杀死他需要额外的一个时间,求救到公主的最短时间
想法:利用优先队列进行广搜
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef struct node
{
int x,y;
int step;
bool operator<(const node &x)const
{
return x.step<step;
}
}node;
node A;
const int N=210;
char g[N][N];
bool vis[N][N];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int n,m;
int sx,sy;//记录骑士初始位置
int minn;
bool bfs()
{
priority_queue<node>q;
node now;
now.x=sx,now.y=sy,now.step=0;
q.push(now);
while(!q.empty())
{
now=q.top();
q.pop();
if(g[now.x][now.y]=='a')
{
A.step=now.step;
return true;
}
for(int i=0;i<4;++i)
{
node ne;
int dx=now.x+dir[i][0];
int dy=now.y+dir[i][1];
ne.x=dx;
ne.y=dy;
if(dx<0||dx>=n||dy<0||dy>=m||vis[dx][dy]||g[dx][dy]=='#')
continue;
vis[dx][dy]=true;
if(g[dx][dy]=='x')
ne.step=now.step+2;
else
ne.step=now.step+1;
q.push(ne);
}
}
return false;
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
minn=0x3f;
memset(vis,false,sizeof(vis));
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
cin>>g[i][j];
if(g[i][j]=='r')
sx=i,sy=j;
}
}
if(bfs())
cout<<A.step<<endl;
else
cout<<"Impossible"<<endl;
}
}