广度搜索(拆点法)

本文介绍了使用广度优先搜索(BFS)解决无线路由器网络优化问题。在给定的平面上,有已放置的路由器和可选的增设位置,目标是最小化从第一个路由器到第二个路由器的中转路由器数量。通过BFS和拆点法BFS,可以找到在增设最多k个路由器后的最优方案。示例输入和输出展示了算法的应用,并提供了不同复杂度的解决方案,包括Floyd算法和BFS实现。
摘要由CSDN通过智能技术生成

广度搜索

概念

广度优先搜索(BFS)是逐层遍历搜索树算法,所有状态按照入队的先后顺序具有层次单调性(步数单调性)。如果每一次扩展恰好对应一步,那么当一个状态第一次被访问时,就得到了从起始状态到达该状态的最少步数。
拆点法BFS是广搜的一种变化形式。如限制某些特殊状态最多不能经历K次,这种形势下,为了到达目标状态,并且所经历的路径符合要求,往往需要重复遍历已经访问过的状态,因此,我们需要把原问题的单个状态点拆分成更多的基于此状态的不同状态,形如[状态i,经历了k个特殊点],方便问题的求解。

例题

问题描述
  目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上。任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。
  除此以外,另有 m 个可以摆放无线路由器的位置。你可以在这些位置中选择至多 k 个增设新的路由器。
  你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。请问在最优方案下中转路由器的最少个数是多少?
输入格式
  第一行包含四个正整数n,m,k,r。(2 ≤ n ≤ 100,1 ≤ k ≤ m ≤ 100, 1 ≤ r ≤ 108)。
  接下来 n 行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线 路由器在 (xi, yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。
  接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi, yi) 点处可以增设 一个路由器。
  输入中所有的坐标的绝对值不超过 108,保证输入中的坐标各不相同。
输出格式
  输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1 个路 由器到第 2 个路由器最少经过的中转路由器的个数。
  
样例输入
13 3 1 3
0 0
5 5
-3 0
-3 2
-3 4
-3 6
-1 7
0 5
3 5
1 -2
3 -2
5 -2
5 1
3 3
4 4
3 0
样例输出
5

解决方案分析

80分方案 佛洛依德最短路算法 部分用例TLE
90分方案 BFS
100分方案 拆点BFS

参考代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

//foyd
typedef long long ll;
int ru[205][205];
int ruk[205][205];

int n,m,K,r;//k
int x[205],y[205];
int selected[205];
bool isSe[205];
int ans= 0x3f3f3f3f;



bool calc(int a, int b)
{
	if( (ll)(x[a] - x[b]) * (x[a] - x[b]) + (ll)(y[a] - y[b])* (y[a] - y[b]) <= (ll)r * r)
	return 1;
	return 0;
}
void floy();
void cnm(int c)
{
	
	if( c > K)
	{
		//CP LINE
		for(int i = 1; i <= K; i ++)
		 {
		 	memcpy(ruk[n+i],ru[selected[i]],sizeof (ruk[n+i]));
		 	for(int j = 1 ; j <= n + m; j ++)
		 	   ruk[j][n+i]   = ruk[n+i][j];
		 }

		//FLOYD
		floy();
		//UPDATE ANSWER
		ans = min(ans,ruk[1][2]);
		//reset ruk
		memcpy(ruk, ru, sizeof ruk);
	}else{
		for(int j= n + 1; j<= n + m; j++)
		{
			if(isSe[j] || j < selected[c-1] ) continue;//组合 
	    isSe[j] = true;
		selected[c] = j;
		
		cnm( c + 1);
		
		selected[c] = 0;
		isSe[j] = false;
		}	
	} 	
}
int main()
{
	memset(ru,0x3f,sizeof ru);
	cin >> n >> m >> K >> r;
	for(int i = 1; i <= n + m; i ++)
		scanf("%d %d",x+i,y + i);	
	
	for(int i = 1; i <= n + m; i ++)
	 for(int j = 1; j <= i; j ++)
	   if(i == j) ru[i][j] = 0;
	   else{
	   	   if(calc(i,j))
			  ru[j][i] = ru[i][j] = 1;
			else ru[j][i] = ru[i][j] = 0x3f3f3f3f;
	   }
	      
	//floyd
	memcpy(ruk, ru, sizeof ruk);
	if(K == m)
	{
	  floy(); 
	  ans = ruk[1][2];
	}else cnm(1);
	cout << ans - 1 << endl;

  return 0;
}
void floy()
{
	for(int k= 1; k < n + K + 1; k ++)
	 for(int i = 1; i < n + K + 1; i ++)
	 {
	 	if(k == i) continue;
	  for(int j = 1; j < n + K + 1; j ++)
	    {
	    	if(i == j || k == j) continue;
	    	if(ruk[i][k] + ruk[k][j] < ruk[i][j])
	    	   ruk[i][j] = ruk[i][k] + ruk[k][j];
		}
      }
}

BFS
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue> 
using namespace std;

//BFS;wirelses net 
typedef long long ll;
ll ru[205][205];

int ans=0;

int n,m,K,r;//k
int x[205],y[205];

bool st[205];

struct element{
	int ii;
	int layer;
	int cnt;//extra elements, occur times
};

bool calc(int a, int b)
{
	if( (ll)(x[a] - x[b]) * (x[a] - x[b]) + (ll)(y[a] - y[b])* (y[a] - y[b]) <= (ll)r * r)
	return 1;
	
	return 0;
	
}
void BFS()
{
	queue<element> q;
    st[1] = true; 
    element a = {1,0,0};
    q.push(a);

    while (q.size())
	{
    element t = q.front();
    
    q.pop();
    int l = t.layer + 1;
    
    for (int i = 1; i <= n + m; i ++)//n + 1... n + m
    {
        if (ru[t.ii][i] && !st[i])//exist road and not visited
        {
        	if(i == 2 && t.cnt > K) continue;

            st[i] = true; 
            //calculate
            if(i>n && i < n + m + 1) q.push({i,l,t.cnt + 1});
            else q.push({i,l,t.cnt});
            //check
            if(i == 2 && t.cnt <= K)
            {
            	ans = l;
            	return ;
		   	}
			
        }
    }
    }
    
}
int main()
{
	
	cin >> n >> m >> K >> r;
	for(int i = 1; i <= n + m; i ++)
	{
		
		scanf("%d %d",x+i,y + i);
		
	}
	
	for(int i = 1; i <= n + m; i ++)
	 for(int j = 1; j <= i; j ++)
	   if(i == j) ru[i][j] = 0;
	   else
	   ru[j][i] = ru[i][j] = calc(i,j);//邻接矩阵 
			
	      
	BFS();

	cout << ans - 1 << endl;

  return 0;
}


BFS + 拆点
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
//BFS;拆点法 
using namespace std;
#define x first
#define y second
const int N = 205,M = N * N;//M is edge number

typedef long long LL;
typedef pair<int, int > PII;

int n, m,k,r;
int h[N],e[M],ne[M],idx;//link table
PII p[N];//save point
int dist[N][N];//from 1 to j,恰好经过k个点的最短路 
//DP
bool calc(PII a, PII b)
{
	LL dx = a.x - b.first;
	LL dy = a.y - b.second;
	return dx * dx  + dy * dy <= (LL) r * r;
	
}
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a];
	h[a] =  idx ++;
}
int bfs()
{
	queue<PII> q;
	q.push({1,0});//pass 1, and no special point
	memset(dist, 0x3f,sizeof dist);
	dist[1][0] = 0;
	
	while(q.size())
	{
	  auto t = q.front();
	  q.pop();
	  
	  for(int i = h[t.x]; ~i; i = ne[i])
	  {
	  	int x = e[i], y = t.y;//x is node no;y is special count
	  	if(x > n) y ++;
	  	if(y <= k)
	  	{
	  		if(dist[x][y] > dist[t.x][t.y] + 1)
	  		{
	  			dist[x][y] = dist[t.x][t.y] + 1;
	  			q.push({x,y});
			}
		}
	  }
		
	} 
	
	int res = 1e7;
	for(int i = 0; i <=k; i++)
	  res = min(res, dist[2][i]);
	return res - 1;
	
	
}
int main()
{ 
  cin >> n >> m >> k >> r;
  memset(h, -1,sizeof h);
  
  for(int i = 1; i <= n;i++)
    cin >> p[i].x >> p[i].y;
    
  for( int i = n + 1; i <= n+m; i ++)
    cin >> p[i].x >> p[i].y;
    
  for(int i = 1; i <= n + m; i ++)
	 for(int j = i + 1; j <= n+m; j ++)
	   if(calc(p[i],p[j]))
	     add(i,j), add(j,i);

  cout << bfs() << endl;
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值