C++搜索算法和曼哈頓距離之最少連通代價

最少聯通代價

Description

在一個N行M列的字符網格上,恰好有 2 個彼此分開的連通塊。每個連通 塊的一個格點與它的上、下、左、右的格子連通。如下圖所示:


現在要把這 2 個連通塊連通,求最少需要把幾個’.’轉變成’X’。上圖的例子中, 最少只需要把 3 個’.’轉變成’X’。下圖用’*’表示轉化爲’X’的格點。


Input

第1行:2個整數N和M(1<=N,M<=50)接下來 N 行,每行M個字符,’X’表示屬於某個連通塊的格點,’.’表示不屬於某個連通塊的格點。

Output

第1行:1個整數,表示最少需要把幾個’.’轉變成’X’。

Sample Input

6 16            
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

Sample Output

3

Hint

1<=N,M<=50

解題

方法一: 深搜+廣搜

這道題可以先用深搜把第一個連通塊的的格點改爲'S',然後廣搜從每個'S'到某個'X'的最短距離(即到第二個連通塊的最短路線長度),在這些結果裏找到最小的,最後輸出。

代碼如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int pre[1000000],a[1000000],b[1000000]; //pre用於存儲廣搜時的前驅,a、b用於存儲位置
int minn,ans=1e10; //minn用與存儲每次廣搜的值,ans用於取最小值
int x[4]={1,-1,0,0},y[4]={0,0,1,-1}; //方向
char map[100][100]; //用於記錄字符網格
bool mark[100][100]; //記錄當前格點是否走過
bool check(int s,int t) //判斷是否可以走到下一個格點
{
	if(s&&t&&s<=n&&t<=m&&!mark[s][t]&&map[s][t]!='S') return 1;
	return 0;
}
void fun(int d) //根據前驅來計算距離
{
	minn++;
	if(pre[d]) fun(pre[d]);
}
void bfs(int r,int c) //計算到第二個連通塊的最短距離仔細寫
{
	memset(mark,0,sizeof(mark)); //清零,以便廣搜
	minn=0; //清零,以便計數
	int head=0,tail=1;
	int nextr,nextc;
	mark[r][c]=1; //標記初始位置
	pre[1]=0; //設置前驅
	a[1]=r;
	b[1]=c;
	while(head!=tail)
	{
		head++;
		for(int i=0;i<4;i++)
		{
			nextr=a[head]+x[i];
			nextc=b[head]+y[i];
			if(check(nextr,nextc))
			{
				tail++;
				a[tail]=nextr;
				b[tail]=nextc;
				mark[nextr][nextc]=1;
				pre[tail]=head;
				if(map[nextr][nextc]=='X')
				{
					fun(tail);
					ans=min(minn,ans);
				}
			}
		}
	}
}
void dfs(int r,int c) //將第一個連通塊的所有格點改爲'S'
{
	for(int i=0;i<4;i++)
		if(check(r+x[i],c+y[i])&&map[r+x[i]][c+y[i]]!='.') //判斷能否對其進行"改造"
		{
			map[r+x[i]][c+y[i]]='S'; //"改造"
			dfs(r+x[i],c+y[i]);
		}
}
void f() //將第一個連通塊的所有格點改爲'S'
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(map[i][j]=='X')
			{
				dfs(i,j);
				map[i][j]='S'; //將當前位置改爲'S'
				return ; //結束函數,以免把第二個連通塊也改爲'S'
			}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",map[i]+1);
	bool flag=1;
	f(); //將第一個連通塊的所有格點改爲'S'
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(map[i][j]=='S')
				bfs(i,j); //計算到第二個連通塊的最短距離
	printf("%d",ans-2); //-2是因爲要刪去起點、終點
}

方法二: 深搜+曼哈頓距離

先用深搜把第一個連通塊的的所有格點改爲'S',再求曼哈頓距離(如圖)。

                             

代碼如下:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int ans=1e10; //ans用於取最小值
int x[4]={1,-1,0,0},y[4]={0,0,1,-1}; //方向
char map[100][100]; //用於記錄字符網格
bool check(int s,int t) //判斷是否可以走到下一個格點
{
	if(s&&t&&s<=n&&t<=m&&map[s][t]!='S') return 1;
	return 0;
}
void dfs(int r,int c) //將第一個連通塊的所有格點改爲'S'
{
	for(int i=0;i<4;i++)
		if(check(r+x[i],c+y[i])&&map[r+x[i]][c+y[i]]!='.') //判斷能否對其進行"改造"
		{
			map[r+x[i]][c+y[i]]='S'; //"改造"
			dfs(r+x[i],c+y[i]);
		}
}
void f() //將第一個連通塊的所有格點改爲'S'
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(map[i][j]=='X')
			{
				dfs(i,j);
				map[i][j]='S'; //將當前位置改爲'S'
				return ; //結束函數,以免把第二個連通塊也改爲'S'
			}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",map[i]+1);
	bool flag=1;
	f(); //將第一個連通塊的所有格點改爲'S'
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=n;k++)
				for(int h=1;h<=m;h++)
					if(map[i][j]=='S'&&map[k][h]=='X')
						ans=min(ans,abs(i-k)+abs(j-h)-1); //運用曼哈頓距離,因重複刪去1
	printf("%d",ans);
}


發佈了89 篇原創文章 · 獲贊 120 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章