详解各类搜索

深搜

inline void dfs(int x)
{
    dfs();
}

广搜

inline void bfs()
{
    queue<int> q;
    q.push(···)
    while(!q.empty())
    {
        ···
    }
}

记忆化搜索

其实就是记录了每一种状态的最优值,达到玄学剪枝效果。。。

例题:单词游戏

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=20;
int n,vis[N],len[N];
string s[N];
int maxx=0,f[N][1<<N];
inline int dfs(int x,int y,int id)
{
	if(f[x][y])return f[x][y];
	int t=0;
	for(int i=1;i<=n;i++)
	{
		if(vis[i])continue;
		vis[i]=1;
		if(id==0||s[id][len[id]-1]==s[i][0])t=max(t,dfs(i,(y|(1<<(i-1))),i));
		vis[i]=0;
	}
	return f[x][y]=t+len[x];
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		len[i]=s[i].size();
	}
	for(int i=1;i<=1000;i++)dfs(0,0,0);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<(1<<n);j++)
		{
			maxx=max(maxx,f[i][j]);
		}
	}
	cout<<maxx;
	return 0;
}

迭代加深搜索

定义

迭代加深搜索也叫 iddfs,是一种每次限制搜索深度的深度优先搜索。

解释

迭代加深搜索的本质还是深度优先搜索,只不过在搜索的同时带上了一个深度 d,当 d 达到设定的深度时就返回,一般用于找最优解。如果一次搜索没有找到合法的解,就让设定的深度加一,重新从根开始。

代码

inline int dfs(int x)//x:深度
{
    if(x>mxd)return ;//mxd:当前设定的最大深度
    ······
    ······
}

例题:Addition Chains

#include <bits/stdc++.h>
using namespace std;
const int N=10005;
int t,n,ans[N],mxd;
inline int dfs(int x)
{
	if(x>mxd)return ans[x-1]==n;
	if(ans[x-1]*(long long)(1ll<<(mxd-x+1))<n)return 0;
	for(int i=0;i<x;i++)
	{
		for(int j=0;j<x;j++)
		{
			int s=ans[i]+ans[j];
			if(s>n)break;
			if(s<=ans[x-1])continue;
			ans[x]=s;
			if(dfs(x+1))return 1;
		}
	}
	return 0;
}
int main()
{
	ans[0]=1;
	while(1)
	{
		scanf("%d",&n);
		if(n==0)break;
		cout<<'1';
		for(mxd=0;;mxd++)
		{
			if(dfs(1))
			{
				for(int i=1;i<=mxd;i++)
				{
					cout<<' '<<ans[i];
				}
				cout<<'\n'; 
				break;
			}
		}
	}
	return 0;
}

注意

  • 在大多数的题目中,广搜还是比较方便的,而且容易判重。当发现广搜在空间上不够优秀,而且要找最优解的问题时,就应该考虑迭代加深
  • 一般在求最少步数类似的题目中可以考虑使用迭代加深

折半搜索(meet in the middle)

简化意思:把暴力分成两部分进行搜索,再把两个的结果融合。

例题:Balanced Cow Subsets G双倍经验

纯暴力:O(3n) 显然不能过

折半搜索:O(3n2) 可以通过~~

思路:总共搜索两次——一次处理前半部分的情况,一次处理后半部分的情况。

代码(题解写法):

#include <bits/stdc++.h>
using namespace std;
const int N=22;
int n,a[N];
map<int,int> id;//由于值域过大,卡了我好久,一直想不到,感觉还挺妙的qwq
int ans[1<<N],tot=0;
vector<int> k[1<<N];
inline void dfs1(int x,int sum,int now)
{
	if(x>n/2)
	{
		if(id[sum]==0)id[sum]=++tot;
		k[id[sum]].push_back(now);
		return;
	}
	dfs1(x+1,sum+a[x],now|(1<<(x-1)));
	dfs1(x+1,sum-a[x],now|(1<<(x-1)));
	dfs1(x+1,sum,now);
}
inline void dfs2(int x,int sum,int now)
{
	if(x>n)
	{
		int u=id[sum];
		if(u!=0)for(int i=0;i<k[u].size();i++)ans[k[u][i]|now]=1;
		return;
	}
	dfs2(x+1,sum+a[x],now|(1<<(x-1)));
	dfs2(x+1,sum-a[x],now|(1<<(x-1)));
	dfs2(x+1,sum,now);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	dfs1(1,0,0);
	dfs2(n/2+1,0,0);
	int cnt=0;
	for(int i=1;i<(1<<n);i++)cnt+=ans[i];
	cout<<cnt;
	return 0;
}

AIDA

A:对当前状态进行估价(是否能够得到答案)

IDA:在 A 的基础上,再加上迭代加深的思想

IDA 例题:骑士精神

#include <bits/stdc++.h>
using namespace std;
const int N=10;
int T,h[N][N],b[N][N],k,n=5;
bool judge=0;
int a[N][N]={
{0,0,0,0,0,0},
{0,1,1,1,1,1},
{0,0,1,1,1,1},
{0,0,0,2,1,1},
{0,0,0,0,0,1},
{0,0,0,0,0,0} 
};
int d[8][2]={{1,2},{1,-2},{2,1},{2,-1},{-2,1},{-2,-1},{-1,2},{-1,-2}};
void init()
{
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)h[i][j]=b[i][j];
}
int test(int s)//估价函数:test
{
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(a[i][j]!=h[i][j])
				if((++sum+s)>k)
					return 0;
		}
	}
	return 1;
}
int check()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(a[i][j]!=h[i][j])return 0;
		}
	}	
	return 1;
}
void A(int s,int x,int y)
{
	if(s==k)
	{
		if(check())judge=1;
		return;
	}
	if(judge==1)return;
	for(int i=0;i<=7;i++)
	{
		int tx=x+d[i][0],ty=y+d[i][1];
		if(tx<1||ty<1||tx>n||ty>n)continue;
		swap(h[x][y],h[tx][ty]);//一定要在if外swap,这里我调了N久
		if(test(s)&&judge==0) A(s+1,tx,ty);
		swap(h[x][y],h[tx][ty]);
	}
}
void solve()
{
	int flag=0,xx=0,yy=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			char c;
			cin>>c;
			if(c=='*')h[i][j]=2,xx=i,yy=j;
			else h[i][j]=c-'0';
			
			b[i][j]=h[i][j];
		}
	}
	judge=0;
	for(k=0;k<=15;k++)//类似迭代加深(iddfs)
	{
		init();
		A(0,xx,yy);
		if(judge)
		{
			cout<<k<<'\n';
			return;
		}
	}
	cout<<-1<<'\n';
	return;
}
int main()
{
	scanf("%d",&T);
	while(T--)solve();
	return 0;
} 
posted @   tyccyt  阅读(19)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示