1. 程式人生 > >【筆記】0-1 bfs

【筆記】0-1 bfs

簡介0-1 bfs

bfs可以O(V+E)求解邊權全為1的圖上最短路。
而當邊權只有0或1時,使用其它最短路演算法是有些浪費的,此時可以使用bfs的變種:0-1 bfs來快速求解,複雜度仍為O(V+E).

01bfs維護一個雙端佇列,當邊權為0時,使用push_front,當邊權為1時,使用push_back.
節點的出隊順序是這樣的:0,0,0,0,0,1,1,1,1,2,2,2,3,3,3,…

01bfs常見於迷宮問題,此外有進階的(1,0),(1,1)版本(見後文).

uva11573 Ocean Currents

n*n的海面上,每個格子都有一個潮水湧動的方向(共8個方向),初始時船在某個位置,每次可以選擇順著水湧動的方向前進,不消耗能量。或者消耗一個能量前往任一一個方向。問從起始位置到目標位置最少需要消耗多少能量。

直接01bfs即可,一個位置可能會重複入隊,但因為鬆弛,它的孩子不會重複入隊,所以可以省略vis陣列。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 1024, MOD = 1000000007;

const int go[8][2]={{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
char save[M][M];
int dis[M]
[M],n,m; int bfs(int str,int stc,int edr,int edc) { memset(dis,0x3f,sizeof(dis)); deque<pair<int,int>> bfs; dis[str][stc] = 0; bfs.emplace_front(str,stc); while(!bfs.empty()) { int r = bfs.front().first, c = bfs.front().second; bfs.pop_front(); if(r==edr&&c==edc) return dis[
r][c]; for(int k=0;k<8;++k) { int nr = r+go[k][0], nc = c+go[k][1]; if(nr>=1&&nr<=n&&nc>=1&&nc<=m) { int nd = dis[r][c]+(k!=save[r][c]-'0'); if(dis[nr][nc] > nd) { dis[nr][nc] = nd; if(nd==dis[r][c]) bfs.emplace_front(nr,nc); else bfs.emplace_back(nr,nc); } } } } return -1; } int main(void) { #ifdef _LITTLEFALL_ freopen("in.txt","r",stdin); #endif n = read(), m=read(); for(int i=1;i<=n;++i) scanf("%s",save[i]+1); int q = read(); while(q--) { int r1=read(),c1=read(),r2=read(),c2=read(); printf("%d\n",bfs(r1,c1,r2,c2)); } return 0; } inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; }

Codeforces 590C Three States

n*m的地圖上有5種格子:1表示國家一,2表示國家二,3表示國家三,.(點號)表示可以修路的地方,#表示不能修路的地方。保證每個國家的面積至少為1且自身連通,現在想要通過修路使三個國家連通,求最少的修路格數。

以每個國家的任意一格為起點,對整張圖跑一遍01dfs,就得到了所有格子分別到三個國家的距離(修路數)。遍歷所有的格子,最小的到三個國家的距離之和就是答案,注意格子為點號時,答案-2,因為這個格子只需要修一次。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 1024, MOD = 1000000007;

const int go[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
char save[M][M];
int dis[4][M][M];
void bfs(int st,int r0,int c0)
{
	deque<pair<int,int>> dq;
	dq.emplace_front(r0,c0);
	dis[st][r0][c0] = 0;
	while(!dq.empty())
	{
		int r = dq.front().first, c = dq.front().second; dq.pop_front();
		for(int k=0;k<4;++k)
		{
			int nr = r+go[k][0], nc=c+go[k][1];
			if(save[nr][nc] && save[nr][nc]!='#')
			{
				int nd = dis[st][r][c] + (save[nr][nc]=='.');
				if(nd<dis[st][nr][nc])
				{
					dis[st][nr][nc] = nd;
					if(nd==dis[st][r][c]) dq.emplace_front(nr,nc);
					else dq.emplace_back(nr,nc);
				}
			}
		}
	}

}
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int n = read(), m=read();
	for(int i=1;i<=n;++i)
		scanf("%s",save[i]+1);
	memset(dis,0x3f,sizeof(dis));
	for(int st=1;st<=3;++st)
	{
		int r0,c0;
		for(int i=1;i<=n;++i) 
			for(int j=1;j<=m;++j)
				if(save[i][j]==st+'0') 
				{
					r0=i, c0=j;
					i=n+1, j=m+1;
				}
		bfs(st,r0,c0);
	}

	int ans = -1;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			if(dis[1][i][j]!=0x3f3f3f3f && dis[2][i][j]!=0x3f3f3f3f && dis[3][i][j]!=0x3f3f3f3f)
			{
				int val = dis[1][i][j] + dis[2][i][j] + dis[3][i][j] - 2*(save[i][j]=='.');
				if(ans==-1||ans>val)
					ans = val;
			}
		}
	}
	printf("%d\n",ans );
    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

GYM100625(BAPC2013) J Jailbreak

n*m的地圖表示一個監獄,*表示牆壁,#表示門,$表示犯人,.(點)表示空地,監獄的外部視為連通的空地,最多有兩個犯人。問最少開啟幾扇門可以使得兩個犯人以及監獄外部連通。

和上題類似,把外部也看成一個獨立的目標即可。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 104, MOD = 1000000007;

const int go[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int n, m, dis[3][M][M];
char mp[M][M]; //地圖

void ZeroOneBfs(int st,int r0,int c0)
{
	deque<pair<int,int>> dq;
	dq.emplace_front(r0,c0);
	dis[st][r0][c0] = 0;
	while(!dq.empty())
	{
		int r = dq.front().first, c = dq.front().second; dq.pop_front();
		for(int k=0;k<4;++k)
		{
			int nr = r+go[k][0], nc = c+go[k][1];
			if(nr>=0&&nr<=n+1&&nc>=0&&nc<=m+1&&mp[nr][nc]!='*')
			{
				int nd = dis[st][r][c] + (mp[nr][nc]=='#');
				if(nd<dis[st][nr][nc])
				{
					dis[st][nr][nc] = nd;
					if(mp[nr][nc]=='#') dq.emplace_back(nr,nc);
					else dq.emplace_front(nr,nc);
				}
			}
		}
	}
}
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int t = read();
	while(t--)
	{
		memset(dis,0x3f,sizeof(dis));
		memset(mp,0,sizeof(mp));

		n = read(), m = read();
		for(int i=1;i<=n;++i)
			scanf("%s",mp[i]+1);

		vector<pair<int,int>>start{{0,0}};
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j)
				if(mp[i][j]=='$')
					start.emplace_back(i,j);
		for(int kase = 0;kase<3;++kase)
			ZeroOneBfs(kase,start[kase].first,start[kase].second);

		int ans = INT_MAX;
		for(int i=0;i<=n+1;++i)
			for(int j=0;j<=m+1;++j)
				if(dis[0][i][j]!=0x3f3f3f3f&&dis[1][i][j]!=0x3f3f3f3f&&dis[2][i][j]!=0x3f3f3f3f)
				{
					int val = dis[0][i][j]+dis[1][i][j]+dis[2][i][j]-2*(mp[i][j]=='#');
					ans = min(ans,val);
				}
		printf("%d\n",ans );
	}

    return 0;
}


inline int read()