最小步數(圖論建模)

cn是大帅哥886發表於2024-08-24
第2題 最小步數 檢視測評資料資訊

小明來到了一個矩形迷官,每個房間寫著一個數字。小明初始在左上角的房間,她準備前往該迷言的右下角房間,每次小明可以向右或者向下行走一步。另外,小明可以進行若干次傳送,每次可以花費1的步數,前往和當前房間不互素的任意一個房間。現在,小明希望你求出從左上角走到右下角的最小步數。你能幫幫她嗎?

輸入格式

第一行兩個整數n,m,表示迷宮有n行m列

接下來n行,每行m個數,a[i][j],表示每個房間的數字

1<=n,m<=500,1<=a[i][j]<=1e5

輸出格式

一個整數

輸入/輸出例子1

輸入:

3 3

1 2 3

2 3 3

3 4 5

輸出:

3

樣例解釋

第一步,向右走一步,當前格子為2.

第二步,傳送到第三行第二列的4.

第三步,向右走一步。

還是考的建模,要如何抽象的變成圖論,建圖

首先看到,如果暴力做,連每個點到每個點的邊,是O(n^4)的,預處理的時候就炸了

所以我們還是,建立虛擬點!以節省複雜度

但是我們發現,只要兩個點能連邊,就是不互素,也就是至少有一個公共因數

但是因數比較多,我們可以考慮質因數。

因為質因數最對只需要最多遍歷6個,就可以找完了。

我們分解質因數:
2 3 5 7 11 13

2*3*5*7*11*13>=1e5

一個數,如果有一個質因數,就和這個質因數點雙向連邊,這樣就確保了“傳送”功能

注意邊權設定1,因為你要實現“傳送”功能,就需要從這個點到質因數點,然後再從質因數點到別的點,要一來一回,花費邊權就是2,具體為什麼看下文。

然後點和別的點(向下,右擴充套件的點)連的的邊權也是2,這樣總體答案除2就行。

或者另外一種方法:

對於一個點x

x->質因數點邊權為1,質因數點->x邊權為0

注意一下,要預處理每個數的質因數,不然在迴圈裡面去找會炸。卡在這裡好久。。

#include <bits/stdc++.h>
using namespace std;
const int N=600;

struct node
{
	int v, w;
	bool operator <(const node &A) const
	{
		return w>A.w;
	};
};
int n, m, c[N][N], vis2[N*N], zhi[N], cnt=0;
int dx[]={0, 1}, dy[]={1, 0};
int dis[N*N], vis[N*N];
vector<node> a[N*N];
vector<int> v[N][N];
priority_queue<node> q;
void dij()
{
	memset(dis, 63, sizeof dis);
	memset(vis, 0, sizeof vis);
	dis[1*501+1]=0;
	q.push({1*501+1, 0});
	
	while (!q.empty())
	{
		int u=q.top().v;
		q.pop();
		
		if (vis[u]) continue;
		vis[u]=1;
		
		for (int i=0; i<a[u].size(); i++)
		{
			int v=a[u][i].v, w=a[u][i].w;
			if (dis[v]>dis[u]+w)
			{
				dis[v]=dis[u]+w;
				q.push({v, dis[v]});
			}
		}
	}
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i=1; i<=n; i++)
		for (int j=1; j<=m; j++) scanf("%d", &c[i][j]);
	
	vis2[1]=1;
	for (int i=2; i*i<=350; i++)
		if (!vis2[i])
			for (int j=i*i; j<=350; j+=i)
				vis2[j]=1;
	
	for (int i=2; i<=350; i++) if (!vis2[i]) zhi[++cnt]=i;
	
	for (int i=1; i<=n; i++)
		for (int j=1; j<=m; j++)
		{
			for (int k=1; k<=cnt && zhi[k]*zhi[k]<=c[i][j]; k++)
				if (c[i][j]%zhi[k]==0) 
				{
					int t=c[i][j]/zhi[k];
					v[i][j].push_back(zhi[k]);
					if (zhi[k]!=t && !vis2[t]) v[i][j].push_back(t);
				}
			if (!vis2[c[i][j]]) v[i][j].push_back(c[i][j]);
		}	
	
	/*
	for (int i=1; i<=n; i++)
		for (int j=1; j<=m; j++)
		{
			printf("[%d, %d] : ", i, j);
			for (int k=0; k<v[i][j].size(); k++)
				printf("%d ", v[i][j][k]);
			puts("");
		}
	*/
	
	for (int i=1; i<=n; i++)
		for (int j=1; j<=m; j++)
		{
			for (int k=0; k<v[i][j].size(); k++)
			{
				a[i*501+j].push_back({v[i][j][k]+260000, 1});
				a[v[i][j][k]+260000].push_back({i*501+j, 1});
			}
			
			for (int k=0; k<2; k++)
			{
				int nx=i+dx[k], ny=j+dy[k];
				if (nx>=1 && nx<=n && ny>=1 && ny<=m)
				{
					a[i*501+j].push_back({nx*501+ny, 2});
					a[nx*501+ny].push_back({i*501+j, 2});
				}
			}
		}
			
	dij();
	
	printf("%d", dis[n*501+m]/2);
	return 0; 
}

  

相關文章