机试指南 cha6 搜索
枚举
百鸡问题
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <map>
#include <string>
#define INFINITY 65535
using namespace std;
const int big = 5;
const int mid = 3;
const float small = 1/3;
int main()
{
int n,x,y,z;
while (cin>>n)
{
for (x = 0;x<=n/big;x++ )
for (y=0;y<=n/mid;y++)
for (z=0;z<=100;z++)
{
float tmp = big*x+mid*y+(float)z/3;
if (x+y+z == 100 && tmp <= n)
{
cout << "x="<<x<<",y="<<y<<",z="<<z<<endl;
}
}
}
return 0 ;
}
z 因为是1/3,
abc
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <map>
#include <string>
#define INFINITY 65535
using namespace std;
const int num = 532;
int cal(int a,int b,int c)
{
return 100*a+10*b+c;
}
int main()
{
int a,b,c;
for (a = 0;a<=9;a++ )
for (b=0;b<=9;b++)
for (c=0;c<=9;c++)
{
int tmp = cal(a,b,c)+cal(b,c,c);
if (tmp == num)
{
cout <<a<<" "<<b<<" "<<c<<endl;
}
}
return 0 ;
}
广搜
胜利大逃亡
提示内存不够:定义时不能多开辟数组空间,形成六面的“墙”,而是要判断是否超出边界的方式;提示运行时间过长,在把孩子结点加入队列后就立即判断是否是终止结点,而不是等到从队列中拿出的时候判断。这道题目的遗憾是1:自己通过思考做的方法就差一点巧妙的构思即可在本地通过编译;2. 牛客网的编译还是过不去,运行时间过长,不知道哪里的错误。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <map>
#include <string>
#define INFINITY 65535
using namespace std;
struct node
{
int x,y,z;
int t;
};
int room[50][50][50];
bool mark[50][50][50];
queue<node> q;
int dir[6][3] =
{
{0,0,1},{0,1,0},{0,0,-1},{0,-1,0},{1,0,0},{-1,0,0}
};
int mazePath(node s,node &e,int a,int b,int c)
{
node t,child;
q.push(s); // 放入初始结点
while (!q.empty())
{
t = q.front(); q.pop();//队头出队
room[t.x][t.y][t.z] = 1; // 已经访问过则不再访问
// 当前节点不为最终节点时,把当前节点的孩子结点入队
for (int i=0;i<6;i++)
{
child = t;
child.x += dir[i][0];
child.y += dir[i][1];
child.z += dir[i][2];
// 下一个结点为墙
if (room[child.x][child.y][child.z] == 1)
continue;
// 超出迷宫边界
if (child.x < 0|| child.x >=a || child.y < 0|| child.y >=b || child.z < 0|| child.z >=c)
continue;
// 包含该坐标的状态已经被得到过,则丢弃
if (mark[child.x][child.y][child.z] == true)
continue;
child.t ++;
q.push(child);
mark[child.x][child.y][child.z] = true; // 标记
if (child.x == e.x && child.y == e.y && child.z == e.z)
{
return child.t;
}
}
}
return 0;
}
int main()
{
int k,a,b,c,t,i,j,m,n;
node start,end,ex;
while (scanf("%d",&k)!=EOF)
{
for (i=0;i<k;i++)
{
scanf("%d%d%d%d",&a,&b,&c,&t);
for (j=0;j<a;j++)
for (m=0;m<b;m++)
for (n=0;n<c;n++)
{
scanf("%d",&room[j][m][n]);
mark[j][m][n] = false; // 初始化为false
}
// (1,1,1) - > (a,b,c)
start.x = 0;start.y=0;start.z=0;
start.t = 0;
mark[0][0][0] = true;
end.x = a-1;end.y=b-1;end.z=c-1;
while (!q.empty())
q.pop();
int ans = mazePath(start,end,a,b,c);
if (ans <= t && ans > 0)
cout << ans << endl;
else
cout << -1 << endl;
}
}
return 0 ;
}
迷宫问题
严版数据结构课本的伪代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <stack>
#include <string>
using namespace std;
// 迷宫求解
// 结构体
typedef struct
{
int x;
int y;
bool foot; // 是否走到过该通道
}posType;
typedef struct
{
int ord;//通道块在路径上的序号
posType seat; // 通道块在迷宫中的坐标
int di;// 从此通道块走向下一通道块的方向
}elemType; // 栈的元素类型
// 全局变量声明
stack<elemType> s;
int a[6][6];
// 函数
void footPrint(posType p)
{
p.foot = true;
}
bool pass(posType p)
{
// 不可通的含义:不是通道;不在当前路径上;不是纳入路径上的通道块
if (a[p.x][p.y] == 1)
return false;
else return true;
}
posType nextPos(posType p,int d)
{
posType tmp;
tmp.x = p.x+1;tmp.y = p.y+1;tmp.foot = p.foot;
return tmp;
}
bool mazePath(int a[6][6],posType start,posType end)
{
// 将从start到end的通道路径存放在栈中,找到路径返回true,否则返回false
while (!s.empty())
s.pop(); // initStack()
posType curpos = start; // 把坐标封装起来
elemType e;
int curstep = 1; // 探索第一步
do {
if (pass(curpos)) // 是未曾走到过的通道块
{
footPrint(curpos); // 留下足迹
e = {curstep,curpos,1}; // 1,2,3,4代表东南西北
s.push(e);
if (curpos == end)
return true;
curpos = nextPos(curpos,1);
curstep++;
}else {
if (!s.empty())// 当前位置不能通过
{
e = s.top();s.pop();
while (e.di == 4 && !s.empty())
{
markPrint(e.seat);//留下不能通过的标记
s.pop();
}
if (e.di < 4)
{
e.di++;
s.push(e);
curpos = nextPos(e.seat,e.di);
}
}
}
}while (!s.empty());
return false;
}
int main()
{
int n,m,i,j;
posType start,end;
while (cin>>n>>m)
{
for (i=0;i<=5;i++)
for (j=0;j<=5;j++)
{
if (i==0||j==0)
a[i][j] = 1;
else
cin >> a[i][j];
}
// (1,1) -> (5,5) 最短路径 输出坐标
end.x = 5;end.y=5;end.foot = false;
start.x = 1;start.y=1;start.foot = false;
mazePath(a,start,end);
}
return 0 ;
}
深搜
#include<iostream>
#include<string>
#include<list>
#define M 8
#define N 8
using namespace std;
/*
每次按照dir的方向进行深度优先搜索,可以走则走并标记,否则,
回溯并清除标记,直到找到右下角的出口。
*/
// 迷宫,Maze[i][j] = 0 代表ij可以走, arr[i][j]=1表示不能走
int Maze[M+2][N+2] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1, 0, 1, 1, 1},
{1, 1, 1, 0, 1, 1, 0, 1, 1, 1},
{1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 1, 0, 0, 1, 1, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 0, 1, 0, 1, 0, 0, 1, 1},
{1, 0, 0, 1, 1, 1, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
// 四个方向,分别代表上,下,左,右,这里注意x和y的方向和笛卡尔坐标系的方向相反
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 节点信息,x代表横坐标,y代表纵坐标
struct Node
{
int x;
int y;
// 初始化位置结点的构造函数
Node (int x1, int y1){
x = x1;y=y1;
}
};
// 打印路径,使用链表,易于输出
void print_path(list<Node> path)
{
while(!path.empty())
{
cout << "(" << path.front().x << "," << path.front().y << ")" << endl;
path.pop_front(); // front()求链表头,pop_front()弹头,我感觉用vector也可以
}
}
int DFS(Node cur, Node end, list<Node> &path) // 当前节点,出口
{
Maze[cur.x][cur.y] = 1; // 标记此节点已走过,设置为墙1
path.push_back(cur); // 将当前节点加入路径栈
// 当前坐标等于结束坐标时结束遍历,并打印路径
if(cur.x == end.x && cur.y == end.y)
{
print_path(path); //打印路径
return 1;
}
// 从4个方向分别探索
for(int i = 0; i < 4; ++i)
{
// 构造下一个要进行探索的点
Node next(cur.x + dir[i][0], cur.y + dir[i][1]);
// 判断下个点是否可行
if(Maze[next.x][next.y] == 0)
{
// 递归进行下一位置的查找
// 如果一直可以先向下查找,直到找到终点,最底层函数就会返回1,接着返回到倒数第二层,执行if语句,接着返回1。
// 直到跳出整个递归函数。
if(DFS(next, end, path) == 1)
return 1;
}
}
// 如果该节点几个方向均已遍历,而且都不可行,该节点出栈,回溯
path.pop_back();
return 0;
}
int main( )
{
list<Node> path; // 存取路径,使用双向链表存储,Node为位置结点
Node sta(1, 1), end(8, 8); // 记录开始和结束坐标
if(DFS(sta, end, path) == 0)
cout << "no path" << endl;
}
广搜
#include <iostream>
#include <fstream>
#include <stack>
#include <vector>
#include <queue>
using namespace std;
const int MAX = 10;
struct node {
int x,y,fx,fy;
int content;
bool visited;
};
node laby[MAX][MAX];
void BFS(int sx, int sy,int ex, int ey) {
queue<node> myQueue;
node temp = laby[sx][sy];
myQueue.push(temp);
int delx[4] = {0,0,-1,1};
int dely[4] = {1,-1,0,0};
while (!myQueue.empty()) {
node currentNode = myQueue.front();
myQueue.pop();
int m = currentNode.x;
int n = currentNode.y;
currentNode.visited = true;
if (currentNode.x == ex && currentNode.y == ey) {
break;
}
for(int i = 0; i < 4;i++) {
int next_m = m + delx[i];
int next_n = n + dely[i];
if (next_m < 0 || next_m >= MAX || next_n < 0 || next_n >= MAX) continue;
if (laby[next_m][next_n].content != 1 && laby[next_m][next_n].visited == false) {
laby[next_m][next_n].fx = m;
laby[next_m][next_n].fy = n;
myQueue.push(laby[next_m][next_n]);
laby[next_m][next_n].visited = true;
}
}
}
int backx = ex, backy = ey;
int step = 0;
stack<node> showStack;
while (backx != sx || backy != sy) {
showStack.push(laby[backx][backy]);
int tempBackx = laby[backx][backy].fx;
int tempBacky = laby[backx][backy].fy;
backx = tempBackx;
backy = tempBacky;
step++;
}
cout<<"Path:"<<endl;
while (!showStack.empty()) {
node current = showStack.top();
showStack.pop();
cout<<'('<<current.x<<','<<current.y<<") ";
}
cout<<endl;
cout<<"total steps:"<<step<<endl;
}
int main(int argc, const char * argv[]) {
ifstream in;
in.open("laby.txt",ios::in);
if (!in) {
cerr<<"file not existed!"<<endl;
exit(1);
}
int sx,sy,ex,ey;
int curNum;
int m = 0, n = 0;
while (!in.eof()) {
in>>curNum;
laby[m][n].content = curNum;
laby[m][n].x = m;
laby[m][n].y = n;
laby[m][n].visited = false;
if (curNum == 5) {
sx = m;
sy = n;
}
if (curNum == 8) {
ex = m;
ey = n;
}
n ++;
if (n == MAX) {
n = 0;
m++;
}
}
for(int i = 0; i < MAX; i++) {
for(int j = 0; j < MAX; j++) {
cout<<laby[i][j].content<<" ";
}
cout<<endl;
}
cout<<endl;
BFS(sx, sy, ex, ey);
return 0;
}
虚拟队列 求最短的路径
#include <iostream>
#include <vector>
#include <stack>
#define M 8
#define N 8
using namespace std;
// 迷宫,Maze[i][j] = 0 代表ij可以走, arr[i][j]=1表示不能走
int Maze[M+2][N+2] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1, 0, 1, 1, 1},
{1, 1, 1, 0, 1, 1, 0, 1, 1, 1},
{1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 1, 0, 0, 1, 1, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 0, 1, 0, 1, 0, 0, 1, 1},
{1, 0, 0, 1, 1, 1, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
// 节点信息,x代表横坐标,y代表纵坐标
struct Node
{
int x;
int y;
Node (int x1, int y1):x(x1), y(y1) {}
};
// 虚拟队列中的节点及其前驱信息
struct QueueElem
{
int pre; // 该节点入队时的前驱节点在虚拟队列中的位置
Node node;
QueueElem(Node node1, int pre1):node(node1), pre(pre1) {}
};
void print_path(vector<QueueElem> &que)
{
stack<QueueElem> s;
QueueElem qe = que.back();
while(qe.pre != -1)
{
s.push(qe);
qe = que[qe.pre];
}
while(!s.empty())
{
qe = s.top();
s.pop();
cout << "(" << qe.node.x << "," << qe.node.y << ")" << endl;
}
}
void maze_shortest(Node start, Node des)
{
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
vector<QueueElem> que; // 用于记录节点入队顺序
int head = 0; // 用于指向队列头部
que.push_back(QueueElem(start, -1)); // 初始点入队,其前驱为-1
while(head < que.size()) // 当虚拟队列非空时
{
Node cur = que[head].node;
for(int i = 0; i < 4; ++i)
{
Node next(cur.x + dir[i][0], cur.y + dir[i][1]);
if(Maze[next.x][next.y] == 0) // 下一节点可走
{
// 下一节点入队
que.push_back(QueueElem(next, head));
if(next.x == des.x && next.y == des.y)
{
print_path(que);
return;
}
}
}
head++; // 虚拟出队
}
}
int main()
{
Node start(1, 1), des(8, 8); // 记录开始和结束坐标
maze_shortest(start, des);
}
2018/5/16 搜索
迷宫问题
这道题做的时候是自己独立思考出来的,但最后却花了更多的时间在调试错误上面,这个OJ用了两个月了,为什么这么基础的问题还要不断的犯错误呢?但调试功底见长是真的,耗时长也是真的,所有的耗时长都是因为练习不够。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <stack>
#include <queue>
using namespace std;
struct node
{
int x,y;
};
int a[12][12];
int dir[4][2] =
{// 东南西北
{0,1},{1,0},{0,-1},{-1,0}
};
queue<node> st;
void printPath();
int mazePath(node s,node e)
{
node next = s;
int ans ;
a[s.x][s.y] = 1;//表示该结点已经走过,记为墙
// 先判断当前节点是否为最终节点
if (s.x == e.x && s.y == e.y)
{
printPath();
return 1;
}
// 当前节点不等于最终节点,最理想的状态是一直往东走
for (int i=0;i<4;i++)
{
// 向四个方向走一步,递归
next = s;
next.x += dir[i][0];
next.y += dir[i][1];
if (a[next.x][next.y] == 0){ // 能走通则压栈,继续走
st.push(next);
ans = mazePath(next,e);
if (ans == 1)
return 1;
}
}
// 如果这一层的四个方向均走不通,出栈
st.pop();
return 0;
}
void printPath()
{
node t;
while (!st.empty())
{
t = st.front();
cout << "(" << t.x-1<<","<<t.y-1<<")"<<endl;
st.pop();
}
}
void printA(int n,int m)
{
for (int i=0;i<=n;i++)
{
for (int j=0;j<=m;j++)
cout << a[i][j] <<" ";
cout << endl;
}
}
int main()
{
int n,m,i,j;
node start,end;
while (cin>>n>>m{
for (i=0;i<=n+1;i++)
for (j=0;j<=m+1;j++)
{// 四周筑墙
if (i==0||j==0 || i==(n+1) || j==(m+1))
a[i][j] = 1;
else
cin >> a[i][j];
}
// (1,1) -> (5,5) 最短路径 输出坐标
start.x = 1;start.y=1;
end.x = n;end.y=m;
while (!st.empty())
st.pop();
st.push(start);
mazePath(start,end);
}
return 0 ;
}
北航机试题
C翻转
这是一道找规律的题目。temp数组的设置比较巧妙。
原矩阵:
1 2 3
4 5 6
7 8 9
顺时针旋转:
7 4 1
8 5 2
9 6 3
数组i=0的变化:
11 - 13
12 - 23
13 - 33
数组i=1的变化:
21 - 12
22 - 22
23 - 32
数组i=3的变化
31 - 11
32 - 21
33 - 31
原矩阵:
1 2 3
4 5 6
7 8 9
逆时针旋转:
3 6 9
2 5 8
1 4 7
数组i=0的变化:
11 - 31
12 - 21
13 - 11
数组i=1的变化:
21 - 32
22 - 22
23 - 12
数组i=3的变化
31 - 33
32 - 23
33 - 13
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <map>
#include <string>
#define INFINITY 65535
using namespace std;
int a[6][6];
void reverse(int op1,int op2,int x,int y)
{
// op1 : 1 顺时针 op2 :2为4个数,3为9个数, 以(x,y)为左上角翻转op2^2个数
int i,j,temp[op2+1][op2+1];
for (i=1;i<=op2;i++)
for (j=1;j<=op2;j++)
{
if (op1 == 1)
{
// 顺时针。按照一行一行从左到右的顺序,依次放入temp中转置后应该在的位置
temp[j][op2+1-i] = a[x+i-1][y+j-1];
}
else
{
temp[op2+1-j][i] = a[x+i-1][y+j-1];
}
}
// 现在temp数组中即为转置后的数组顺序,只要赋值到a中相应的位置并输出即可
for (i=x;i<x+op2;i++)
for (j=y;j<y+op2;j++)
a[i][j] = temp[i-x+1][j-y+1];
}
int main()
{
int i,j,op1,op2,x,y;
for (i=1;i<=5;i++)
for (j=1;j<=5;j++)
cin >> a[i][j];
cin >> op1 >> op2 >> x >> y;
reverse(op1,op2,x,y);
for (i=1;i<6;i++)
{
for (j=1;j<6;j++)
{
cout << a[i][j]<< " ";
}
cout << endl;
}
return 0 ;
}
旋转矩阵
这道题我的第一个想法是:把第一个矩阵旋转四个角度,看得到的矩阵是否和第二个矩阵相等,但是这样太麻烦了!判断标准有两个,正确旋转结果相同或者存在一个错误旋转结果,则直接否定该种旋转方案。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <map>
#include <string>
#define INFINITY 65535
using namespace std;
int a[10][10],b[10][10];
int main()
{
int n,i,j;
bool flag[4] = {true,true,true,true}; // 0 90 180 270
cin >> n;
for (i=0;i<n;i++)
for (j=0;j<n;j++)
cin >> a[i][j];
for (i=0;i<n;i++)
for (j=0;j<n;j++)
cin >> b[i][j];
for (i=0;i<n;i++)
for (j=0;j<n;j++)
{
// 对所有的a[i][j]进行判别
if (a[i][j] != b[i][j])
flag[0] = false;
if (a[i][j] != b[j][n-1-i])
flag[1] = false;
if (a[i][j] != b[n-1-i][n-1-j])
flag[2] = false;
if (a[i][j] != b[n-1-j][i])
flag[3] = false;
}
for (i=0;i<4;i++)
if (flag[i] == true)
{
cout << i*90 << endl;
return 0 ;
}
cout << -1 << endl;
return 0 ;
}
字符串匹配
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <string.h>
#define INFINITY 65535
using namespace std;
string a[1000];
string lower(string a)
{
for (int i=0;i<a.length();i++)
{
if (a[i]>= 'A' && a[i] <= 'Z')
a[i] = a[i] - 'A'+'a';
}
return a;
}
void match(int n,string b)
{
for (int i=0;i<n;i++)
{
if (lower(b) == lower(a[i]))
{
cout << i+1 << " " << a[i]<<endl;
}
}
}
int main()
{
int n,i;
string b, m, tmp1, tmp2, new1;
while (cin>>n)
{
for (i=0;i<n;i++)
cin >> a[i];
cin >> b;
// b中可以有一个模式匹配,中括号内的字符中的任意一个即可
int pos1 = b.find("["); // 找到返回索引,否则返回-1
int pos2 = b.find("]"); // 找到返回索引,否则返回-1
if (pos1 == -1 )
{
match(n,b);
}
else
{
m = b.substr(pos1+1,pos2-pos1-1);
tmp1 = b.substr(0,pos1);
tmp2 = b.substr(pos2+1,b.length()-pos2);
for (i=0;i<m.length();i++)
{
new1 = tmp1+m[i]+tmp2;
match(n,new1);
}
}
}
return 0 ;
}
通过率大概百分之18,题目描述地不太清楚。