搜索的快乐骗分
10月1日
大家好,这里是椋枨(分度值),今天我们就来了解一下搜索的相关内容,希望大家不要嫌弃(虽然内容啥都不是)
分类
-
- 最优解
-
- 解数量
-
- 解方案
方法
1. DFS(大法师)
- 转移代价不为1
2. BFS(白法师)
- 转移代价为1
- 最优解问题
- 如 01迷宫 其中每次转移时可到达的格子总数都会加1,符合BFS条件
剪枝
1. 可行性剪枝
-
2. 最优解剪枝
-
3. 对称性剪枝
- 如 八皇后问题
因为棋盘是对称的,所以只需要搜一半的数据
BFS的奇妙优化
双向广搜
- 如 八数码难题
因为空格‘0’转移前后的两种状态是互逆的,所以在这里我们将采用双向宽搜来解决问题,我们将用一个结构体&队列f来存储相关数据,f[i].map[j][k]表示这个队列里的第i组数据的状态的第j行k列。
并且,我们用wi和wj来表示0所在的位置的纵坐标以及横坐标,用t来表示步数,我们用f[1]来存储开始的状态,用f[2]来存储结束的状态。
接着,我们用c来表示它是从起始状态开始延伸过来的还是从目标状态开始延伸过来的。若c=1就表示它是从起始状态开始延伸过来的,否则当c=2时就表示它是从目标状态开始延伸过来的。所以当搜索新的状态时发现有两个重复的话就判断一下他们的c是否相等,若我们用now来表示与新搜索到的状态相同的状态的位置,tou表示当前的状态,那么答案等于f[now].c+f[tou].c+1
while(tou<wei)
{
if(zp(tou)==true)
{
printf("%d",f[tou].t);
return ;
}
for(int i=0;i<=3;i++)
{
int nx=f[tou].wi+dx[i];
int ny=f[tou].wj+dy[i];
if(nx>=1 && nx<=3 && ny>=1 && ny<=3)
{
memset(nz,0,sizeof(nz));
for(int j=1;j<=3;j++)
{
for(int k=1;k<=3;k++)
{
nz[j][k]=f[tou].map[j][k];
}
}
nz[f[tou].wi][f[tou].wj]=f[tou].map[nx][ny];
nz[nx][ny]=0;
if(pdc()==true)
{
for(int j=1;j<=3;j++)
{
for(int k=1;k<=3;k++)
{
f[wei].map[j][k]=nz[j][k];
}
}
f[wei].t=f[tou].t+1;
f[wei].wi=nx;
f[wei].wj=ny;
paint();
if(zp(wei)==true)
{
printf("%d",f[tou].t+1);
return ;
}
wei++;
}
}
}
tou++;
}
位运算优化
八皇后问题
(怎么还是你)
- 在这里,我们可以用一个8位的二进制数来表示当前行的状态。若某一位上为‘0’,说明当前位置可以放皇后;相反,若是为‘1’,说明当前位置不可放皇后。同时,我们将当前行的二进制数左移(<<1)或右移(>>1)一位,就是下一行的状态。
奇技淫巧 (玄学优化)
1. 改变搜索顺序
- 搜索的结果与顺序无关,但越早找到最优解,最优性剪枝的力度就越大
- 如经典问题:靶形数独
据说当时出题人卡了从第一行暴搜到最后一行的数据,结果当时有个脑回路清奇的选手从最后一行暴搜到第一行。
然后:30->85(懂了,以后都倒着搜索)
2. 随机打乱
#include <algorithm>
using namespace std;
random_shuffle(z+1, z+n+1); //打乱z序列
3. 卡时(只能用在最优解问题)
- 应付TLE问题
- NOIP有人实战过,分数由30->80,很强的骗分手段
#include <ctime>
#include <iostream>
using namespace std;
void dfs () {
//逼真的dfs
if (clock() >= 900) //卡900毫秒
{
//逼真的输出
exit(0); //等价于return 0; 直接退出程序;
}
}
int main () {
while (clock() <= 900); //硬核卡900毫秒
cout << clock () << endl;
//因为退出程序会耗费一定时间
//所以要留出一定时间给程序来办理后事。
//所以如果题目的时限为1000毫秒,若卡980毫秒的话,也是有可能超时的。所以尽量卡900毫秒左右
}
作业:
- 你谷P1074,
- 你谷P1379(BFS和双向BFS),
- 你谷P1219(对角线重定义和位运算优化都写)
拓展:
- 你谷P1312