*题解:QOJ #17855. Crosslink

题目链接

解析

考虑只要求指定的两边联通怎么做,发现可以建两个超级源点分别连上两条边上的所有点,分别从两个源点开始跑 Dijkstra,最后枚举网格中的所有点即可。虽然到达两个边的路径可能会重,但出现这种情况的点必定更劣,且更优的点必定能被考虑到,所以不用另外处理这种情况。

对于四边联通,可以将其看成是两个两边联通。于是不难想到建四个超级源点跑四次最短路,对于每个点,对所有两条边的组合处理出最小到达花费。但是接下来要选择两个点分别作为连接两个边的代表,并计算使得它们联通的花费,光是枚举点的复杂度就已经不能接受。

依旧考虑最短路,令 \(dis_{i,j}\) 表示联通块包含 \(i\) 且能到达的边界集合为 \(j\) 时的最小花费,以一个连接所有点的超级源点跑最短路即可。

时间复杂度 \(O(nm\log nm)\)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
typedef pair<int,int> pii;
int mp[N][N],dis1[4][N][N],vis[N][N];
int rev[N];
int n,m;
int xy[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
struct S{
	int dis,x,y;
	friend bool operator > (S a,S b){
		return a.dis > b.dis;
	}
};
void dij1(int dis[N][N],char c){
	memset(vis,false,sizeof(vis));
	memset(dis,127,sizeof(int) * N * N);
	priority_queue<S,vector<S>,greater<S> > pq;
	if(c == 'U'){
		for(int i=1;i<=m;i++){
			dis[1][i] = mp[1][i];
			pq.push({dis[1][i],1,i});
		}
	}else if(c == 'D'){
		for(int i=1;i<=m;i++){
			dis[n][i] = mp[n][i];
			pq.push({dis[n][i],n,i});
		}
	}else if(c == 'L'){
		for(int i=1;i<=n;i++){
			dis[i][1] = mp[i][1];
			pq.push({dis[i][1],i,1});
		}
	}else if(c == 'R'){
		for(int i=1;i<=n;i++){
			dis[i][m] = mp[i][m];
			pq.push({dis[i][m],i,m});		 
		}
	}
	while(!pq.empty()){
		int x = pq.top().x,y = pq.top().y;
		pq.pop();
		if(vis[x][y]) continue;
		vis[x][y] = true;
		for(int i=0;i<4;i++){
			int nx = x + xy[i][0],ny = y + xy[i][1];
			if(nx >= 1 && nx <= n && ny >= 1 && ny <= m){
				if(dis[nx][ny] > dis[x][y] + mp[nx][ny]){
					dis[nx][ny] = dis[x][y] + mp[nx][ny];
					pq.push({dis[nx][ny],nx,ny});
				}
			}
		}
	}
}
bool vis2[N][N];
int dis2[6][N][N];
vector<int> v;
void dij2(int dis2[N][N]){
	memset(vis2,false,sizeof(vis2));
	priority_queue<S,vector<S>,greater<S> > pq;
	for(int j=1;j<=n;j++){
		for(int k=1;k<=m;k++){
			pq.push({dis2[j][k],j,k});
		}
	}
	while(!pq.empty()){
		int x = pq.top().x,y = pq.top().y;
		pq.pop();
		if(vis2[x][y]) continue;
		vis2[x][y] = true;
		for(int i=0;i<4;i++){
			int nx = x + xy[i][0],ny = y + xy[i][1];
			if(nx >= 1 && nx <= n && ny >= 1 && ny <= m){
				if(dis2[nx][ny] > dis2[x][y] + mp[nx][ny]){
					dis2[nx][ny] = dis2[x][y] + mp[nx][ny];
					pq.push({dis2[nx][ny],nx,ny});
				}
			}
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	freopen("crosslink.in","r",stdin);
	freopen("crosslink.out","w",stdout); 
	cin>>n>>m;
	for(int i=0;i<(1 << 4);i++){
		if(__builtin_popcount(i) == 2){
			rev[i] = v.size();
			v.push_back(i);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			char c;
			cin>>c;
			if(c >= 'A' && c <= 'Z'){
				mp[i][j] = c - 'A' + 10;
			}else{
				mp[i][j] = c - '0';
			}
		}
	}
	dij1(dis1[0],'U');
	dij1(dis1[1],'D');
	dij1(dis1[2],'L');
	dij1(dis1[3],'R');
	for(int i=0;i<(int)v.size();i++){
		int p1 = 0,p2 = 0;
		int cnt = 0;
		for(int j=0;j<4;j++){
			if(v[i] & (1 << j)){
				cnt++;
				if(cnt == 2) p2 = j;
				else p1 = j;
			}
		}
		for(int j=1;j<=n;j++){
			for(int k=1;k<=m;k++){
				dis2[i][j][k] = dis1[p1][j][k] + dis1[p2][j][k] - mp[j][k];
			}
		}
	}
	for(int i=0;i<6;i++){
		dij2(dis2[i]);
	}
	int res = 2e9;
	for(int k=0;k<6;k++){
		int l = rev[v[k] ^ (0b1111)];
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				int x = dis2[k][i][j] + dis2[l][i][j] - mp[i][j];
				res = min(res,x);
			}
		}
	}
	cout<<res;
	return 0;
}
posted @ 2026-09-05 20:03  yutar  阅读(14)  评论(0)    收藏  举报