广度搜索
概念
广度优先搜索(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;
}