题目链接:https://vijos.org/p/1016
首先简单介绍下题目。就是有9个挂钟,时间只存在3,6,9,12 这4种状态对应的 状态编号是 1,2,3,0,然后给你9种操作时钟的方式,每次可以事操作的时钟状态编号+1,如果编号到了4,就是表示为0。目标就是把9个闹钟全部变为0状态。题目大致是这个意思,不懂的可以看看上面的题目链接。
首先我从我比较熟悉的DFS讲起,这是深度优先搜索,它的优点就是如果目标答案需要很多步骤完成,这样有几率一次就完成了,不过缺点在于需要每次都是把所有操作数都枚举一遍(0~4)之间,0表示不使用当前操作,但是你还是得在代码里面体现。
DFS好理解,但是所谓的不必要的堆栈开销,严重影响计算机性能,可能你对于简单的操作步骤,和复杂的操作步骤,搜索时间可能相差不大。
而BFS相当于,如果步骤简单可以很快得出结果,步骤复杂就是很慢。这样就对应了需要计算的规模,但是编程复杂度还是稍高一些,也可能不太理解。
下面我写两个demo
DFS 这边可以不保存每次的状态信息。也就是状态信息可以不用数组存储起来。有两种方法处理,不同的状态1.你可以用公用的全局变量保存,但是需要回溯,这个优先,不会错。2,你可以使用boolean dfs() 用return的方法返回当前结果的值,这个不需要回溯,但是使用boolean 来判断搜索是否正确。也可以使用返回值,这个也可以但是个人感觉不好把握。回头研究下有返回值的DFS
public void dfs(int d){
int (d==k)//如果搜索深度到了指定的k、直接优先返回,我觉得这样写最标准
{
if(sum == target){ //这里需要判断是否搜索到了结果 如果搜索到了 加一个全局flag标志
flag = true;
return ;
}
return;//不管搜索到 都是返回
}
for(int i =0;i<=3;i++)//注意这边需要带0 表示当前节点不使用。
{
sum+ = i*mm[d];//mm[d]表示当前深度的搜索节点值,可以搜0到3次,0次代表不搜索,结果放sum里面
save[d] = i;//这里用全局save 保存当前节点值的次数。保存结果而已
dfs(d+1);//搜索下一层
sum-= i*mm[d];//这边注意回溯下
}
if(flag)//这里一次搜索结束判断是否 全局flag为true
{
return ; //搜索到结果可以直接ruturn 或者你可以等全部搜索完成,记录下最小值。这边可以自己发挥。每次搜索到结果 都和min比较下,如果小就留下,并且更新min的值 和全局保存结果的数组
}
}
//基本上我认为的DFS写完了,可能有不对的地方。。。
BFS的demo我不太熟练,因为我不经常用BFS,但是可以大概描述下 https://blog.csdn.net/jiange702/article/details/81365005 这篇文章介绍的很不错
bfs就是利用队列形式实现,相当于收工模拟,他就是需要每次都要保存状态信息,因为两个搜索之间事没有联系的。只有搜索到了最后结果,才会把当前记录的拿出来。而且不依赖全局变量存储,只需要从自己的变量里面取就好了。所以对于BFS我们在c语言中需要定义结构体,而不是全局边量。但是java中没有结构体,我们可以利用map存一下吧。因为队列里的搜索每一个都是独立的个体,所以不能使用全局变量。我们可以用Hashmap里面保存一下几个值{当前状态信息 value可能是数组list 也可以使用二进制表示状态,搜索了几步 这个可以用int,每一步搜索的具体形式(相当于每一步都搜索了哪些值)这个用数组} ,每次我们都拿当前状态信息比较目标状态,如果匹配,就可以输出答案,并且结束循环。当然你也可以先结束循环,比如先拿数组第一个元素,比较是否是目标信息,不是就出队列。如果是,就return; 然后输出数组第一个元素的相关信息。
优点就是根据数据规模来运行时间,但是保存的东西太多了。。。。可能会出错。而且内存也占用太大。 当然肯定有优化,也可以使用全局来优化,map里面可以只保存 步骤和 每一步怎么操作的。然后根据这些信息算出状态。。还不如每次保存呢。。。。。。
下面给出demo
public void BFS(){
ArrayList my = Arrays.copy(m);//这边m表示要操作的初始状态数组,首先复制一下,不然原始数据没了
Map first = new HashMap(4);//这边需要先第一个节点入队列 这个节点保存初始信息
first.add("now_status",my);//这边是保存初始状态
first.add("pos",0);//这边保存多少搜索多少步骤,如果不需要输出可以不要
first.add("operate",new ArrayList(pos));//这个数组是操作数组,代表每步搜索了啥,初始肯定是空
queue.push(first);//这边自己定义个队列,然后入队列
visit[my] = true;
pos=0;//全局操作步骤
while(!queue.isEmpty()){//队列不为空,如果为空表示搜索完了还没结果
Map a = queue.pop();//取第一个
if(a.get("now_status") == target_status)
{
print;//这边输出结果 通过 a.get("operate").get(pos);输出拉 ,步骤肯定是最小的
return ;
}
pos++;
for(int i =1; i<=9; i++)
{
status = a.get("now_status")+= mm[i];//mm[i]表示每个操作
if(!visit[status])//新的状态加入队列 老状态不加
{
//构造map
map.put("pos",pos);
//保存操作的mm[i]
map.put("operate","")
//存入新的status
map.put("now_status",status );
queue.push(map);
}
}
//结束
}
}
差不多BFS没了 下面给出我认为比较好的BFS和DFS算法 复制的哦
DFS
#include
#include
#include
#include
#include
using namespace std;
bool flag[4][4][4][4][4][4][4][4][4];
int op[10][10] = {{0},{1,2,4,5},{1,2,3},{2,3,5,6},{1,4,7},{2,4,5,6,8},
{3,6,9},{4,5,7,8},{7,8,9},{5,6,8,9}};
int st[10];
struct node
{
int state[10];
int pos;//从起始状态到当前状态经过的操作数
int ans[200];//从起始状态到当前状态所有的操作
friend bool operator < (struct node a,struct node b)
{
return a.pos > b.pos;
}
}s,now;
void Bfs()
{
priority_queue
int i,j;
memset(flag,0,sizeof(flag));
for(i = 1;i <= 9;i ++)
s.state[i] = st[i];
s.pos = 0;
flag[st[1]][st[2]][st[3]][st[4]][st[5]][st[6]][st[7]][st[8]][st[9]] = 1;
lcm.push(s);
while(!lcm.empty())
{
now = lcm.top();
lcm.pop();
for(i = 1;i <= 9;i ++)
{
s = now;
for(j = 0;op[i][j];j ++)
{
s.state[op[i][j]] ++;
if(s.state[op[i][j]] >= 4)
s.state[op[i][j]] -= 4;
// s.state[op[i][j]] %= 4;
}
if(!flag[s.state[1]][s.state[2]][s.state[3]][s.state[4]][s.state[5]][s.state[6]][s.state[7]][s.state[8]][s.state[9]])
{
s.ans[s.pos ++] = i;
flag[s.state[1]][s.state[2]][s.state[3]][s.state[4]][s.state[5]][s.state[6]][s.state[7]][s.state[8]][s.state[9]] = 1;
lcm.push(s);
}
if(!s.state[1] && !s.state[2] && !s.state[3] && !s.state[4] && !s.state[5]
&& !s.state[6] && !s.state[7] && !s.state[8] && !s.state[9])
{
// printf("%d",s.ans[0]);
sort(s.ans,s.ans + s.pos);//所有的操作互不干扰,无先后之分的
for(j = 0;j < s.pos;j ++)
printf("%d ",s.ans[j]);
printf("\n");
return;
}
}
}
}
int main()
{
int i,j;
while(scanf("%d",&j) != EOF)
{
st[1] = j;
for(i = 2;i <= 9;i ++)
{
scanf("%d",&st[i]);
}
Bfs();
}
return 0;
}
下面是我自己写的dfs 感觉思路很清晰 可以给各位大佬参考下
import java.util.Scanner;
public class Main {
int[][] target = {{0},{1,2,4,5,0},{1,2,3,0},{2,3,5,6,0},{1,4,7,0},{2,4,5,6,8,0},{3,6,9,0},{4,5,7,8,0},{7,8,9,0},{5,6,8,9,0}};
int[] output = new int[10];
int[] record = new int[10];
boolean flag = false;
public void dfs(int deep) {
if(deep == 10) {
flag = check();
return ;
}
for(int i =0; i<=3; i++) {
change(deep,i);
record[deep] = i ;
dfs(deep+1);
if(!flag) {
change(deep ,-i);
}
else {
return;
}
}
}
public boolean check() {
for(int i =1; i<=9; i++) {
if(output[i]!=0) {
return false;
}
}
return true;
}
public void change(int deep, int i ) {
if(i !=0) {
int j=0;
while(target[deep][j]!=0)
{
output[target[deep][j]] +=4;//不够减
output[target[deep][j]] += i;
output[target[deep][j]] %= 4;
j++;
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int i=1;
Main problem = new Main();
while(i<=9) {
problem.output[i] = sc.nextInt();
problem.record[i] = 0;
i++;
}
problem.dfs(1);
for(i=1; i<=9; i++) {
while(problem.record[i]>0) {
System.out.print(i+" ");
problem.record[i]--;
}
}
}
}
BFS
#include
using namespace std ;
//Vijos P1016
const int way[9][9] = {{1 , 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0} , {1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0} , {0 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 0} , {1 , 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0} , {0 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 0} , {0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1} , {0 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , 0} , {0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1} , {0 , 0 , 0 , 0 , 1 ,1 , 0 , 1 , 1}} ;//9种方式改变不同的钟,1代表加3点,0代表不变
bool judge ;//判断是否调整完成
int x[9] , ans[9] ;
void dfs(int num)
{
if (num == 9)//9种方式全部搜完
{
for (int i = 0 ; i < 9 ; i ++)
{
if (x[i] % 4 != 0)//有不是12点的钟
return ;
}
judge = true ;//全部调整完成
return ;
}
for (int i = 0 ; i <= 3 ; i ++)
{
ans[num] = i ;//第(i+1)钟方式的数量
for (int j = 0 ; j < 9 ; j ++)
{
x[j] += way[num][j] * i ;//改变
}
dfs(num + 1) ;//深搜
if (judge)//此时已经调整完成,而调整方式有且只有一种,因此可以输出答案
return ;
for (int j = 0 ; j < 9 ; j ++)
{
x[j] -= way[num][j] * i ;//回溯
}
}
}
int main()
{
for (int i = 0 ; i < 9 ; i ++)
{
cin >> x[i] ;
}
dfs(0) ;
for (int i = 0 ; i < 9 ; i ++)
{
for (int j = 0 ; j < ans[i] ; j ++)
{
cout << i + 1 << ' ' ;
}
}
cout << endl ;
}