搜索优化——实用的算法,玄学的复杂度

本文主要通过例题讲述搜索的几种优化方式,包括 DFS 的迭代加深,IDAstar,BFS 的优先队列优化,双向 BFS 以及 Astar。一、广度优先搜索1.1 双端队列 BFS例题1 求第 k+1k+1k+1 长边最短路。题目链接由于边权 w≤106w\le 10^6w≤106,可以二分得到第 k+1k+1k+1 长边的最短可能长度。因此,本题转换成了求第 k+1k+1k+1 长边长度为 lenlenlen 的可能性。时间复杂度要求 O(nlog⁡w)O(n\log w)O(nlogw),因此需要
摘要由CSDN通过智能技术生成

本文主要通过例题讲述搜索的几种优化方式,包括 DFS 的迭代加深,IDAstar,BFS 的优先队列优化,双向 BFS 以及 Astar。

一、广度优先搜索

1.1 双端队列 BFS

例题1 求第 k + 1 k+1 k+1 长边最短路。题目链接

由于边权 w ≤ 1 0 6 w\le 10^6 w106,可以二分得到第 k + 1 k+1 k+1 长边的最短可能长度。因此,本题转换成了求第 k + 1 k+1 k+1 长边长度为 l e n len len 的可能性。时间复杂度要求 O ( n log ⁡ w ) O(n\log w) O(nlogw),因此需要在线性复杂度内判断可行性。

判断时,将所有边权 > l e n >len >len 的边权值设为 1 1 1 ≤ l e n \le len len 的边权值设为 0 0 0,则 s → t s\to t st 的最短路若不超过 k k k,则代表只有不超过 k k k 条比其长的边,因此其可能成为 k + 1 k+1 k+1 长边。

在迭代时,使用双端队列储存所有遍历到的结点,若权值为 1 1 1 则从队尾入队,否则从队首入队,每次取队首进行扩展,即可得到 01 权图的最短路。

bool vis[maxn];
int dis[maxn],n,m,k,s,t;
vector<int> e[maxn],w[maxn];//模拟邻接表
bool check(ll len){
   
	fill(dis,dis+n+10,inf);
	memset(vis,0,sizeof(vis));//初始化
	deque<int> q;//双端队列
	dis[s]=0;q.push_back(s);
	while(!q.empty()){
   
		ll u=q.front();q.pop_front();
		if(u==t) return dis[t]<=k;//到达汇点则返回可行性
		if(vis[u]) continue;
		vis[u]=1;
		for(ll i=0;i<e[u].size();i++){
   
			ll v=e[u][i];
			if(!vis[v]){
   
				ll c=(w[u][i]>len);//将边权转化为 01
				if(dis[v]<=dis[u]+c) continue;
				dis[v]=dis[u]+c;
				if(c) q.push_back(v);
				else q.push_front(v);//根据边权从队首或队尾入队
			} 
		}
	}
	return 0;
}
ll Binary(ll l,ll r){
   //二分答案
	ll ans=inf;
	while(l<=r){
   
		ll mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}

1.2 优先队列 BFS

对于每次扩展的代价不同时,采用优先队列(堆)优化迭代。每次取出当前代价最小的状态进行扩展,将新状态加入优先队列,反复迭代直至队列为空。

每个状态第一次被取出时,即为其最小代价,因此每个状态只扩展一次,复杂度为 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn),经典算法形如 Dijkstra。

例题:POJ 3635

n n n 个城市和 m m m 条道路构成无向图,每个城市有一个加油站,价格不同。每条道路的油耗即为其权值,求多询问,对于容量为 C C C 的车, s → t s\to t st 最少花多少钱。

使用拆点的思想,将每个点拆成 C C C 个数对 ( i d , f u e l ) (id,fuel) (id,fuel),表示 (编号,剩余油量),且 d i s [ i d ] [ f u e l ] dis[id][fuel] dis[id][fuel] 表示最少花费。 对于每次询问进行优先队列 BFS(即 Dijkstra),起始状态为 ( s , 0 ) (s,0) (s,0),每个状态课扩展出:

  1. f u e l < C fuel<C fuel<C 可以加 1 1 1 升油,花费为 p r i c e i d price_{id} priceid
  2. 若边权 w ( i d , v ) ≤ f u e l w(id,v)\le fuel w(id,v)fuel,则可以扩展到 ( v , f u e l − w ) (v,fuel-w) (v,fuelw)

不断取出花费最少的状态进行扩展,直到 t t t 被取出即可。复杂度 O ( ( n C + m ) log ⁡ n C ) O((nC+m)\log nC) O((nC+m)lognC)

int n,m,price[maxn];
int tot,ver[M],head[maxn],wth[M],nxt[M];
inline void add(int u,int v,int w){
   
	ver[++tot]=v;wth[tot]=w;nxt[tot]=head[u];head[u]=tot;
	ver[++tot]=u;wth[tot]=w;nxt[tot]=head[v];head[v]=tot;
}
struct node{
   
	int d,u,c;
	bool operator <(const node &x)const{
   
		return d>x.d;
	}
};
int dist[maxn][C];
bool vis[maxn][C];
inline int dijkstra(int s,int t,int cap){
   
	memset(dist,0x3f,sizeof(dist));
	priority_queue<node> q;
	memset(vis,0,sizeof(vis));
	q.push({
   0,s,0});
	while(!q.empty()){
   
		node x=q.top();q.pop();
		if(x.u==t) return x.d;
		if(vis[x.u][x.c]==1) continue;
		vis[x.u][x.c]=
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值