[CF1753D]The Beach

做题时间:2022.10.24

\(【题目描述】\)

给定一个 \(n\times m(n,m\leq 3\times10^5,n\times m\leq 3\times 10^5)\) 的网格图,上面有的是空地,有的是障碍 # ,有的是大小为 \(1\times2\) 的床,现在可以花费 \(p(p\leq 10^9)\) 的代价将其中一个床绕其中某个点旋转 \(90°\),也可以花费 \(q(q\leq 10^9)\) 的代价将其中一个床延长边移动一个单位。现在要腾出相邻的两个为空地的点,问最小代价是多少,或告知无解。

\(【输入格式】\)

第一行两个整数 \(n,m\)

第二行两个整数 \(p,q\)

接下来 \(n\) 行每行一个长为 \(m\) 的字符串,表示一个 \(n\times m\) 的网格图,其中 . 表示空地, # 表示障碍,L R U D 分别表示一张床的左边、右边、上面、下面的点

\(【输出格式】\)

第一行一个整数表示答案,若无解输出 -1

\(【考点】\)

最短路

\(【做法】\)

看到这种网格图上相邻两点成一体的题,可以想到网格图上奇偶染色,相邻的点归属不同的颜色,然后跑二分图,然后发现床的移动依然不好维护。

转化一下思路,因为最后要求的是得到相邻空地的代价,因此可以考虑维护空地的移动,对于一张床,我们可以花费 \(p\) 的代价旋转 \(90°\),意味着可以将与床长边相邻的空地移动到不与其相邻的床上的点;花费 \(q\) 的代价延长边移动一格,意味着将与床短边相邻的空地移动到与其不相邻的床上的点。如图:


代价分别为 \(p\)\(q\) 的移动(红色点为可移动空地,竖着的床同理)

然后通过观察会发现,移动只会发生在之前染成同一种颜色的点之间,即不同颜色的点的移动互不影响。最后求解的是相邻两点,也是不同颜色的。这启发我们将网格图上的每一个点看成图上的点,而可以移动的点之间连边(注意是单向边),长度即为移动代价,初始所有空地为起点,跑一遍最短路,求出图上所有相邻两个点到起点的最短距离之和即可。时间复杂度 \(\Theta(nm\log nm)\)

\(【代码】\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=6e5+50;
typedef long long ll;
struct edge{
	int to,nxt,val;
}a[N<<1];
struct point{
	int num;
	ll val;
	bool operator <(const point &b) const{
		return val>b.val;
	}
};
ll dis[N];
int head[N],cnt,n,m,p,q;
priority_queue<point> que;
string s[N];
bool vis[N];
inline int id(int x,int y){return (x-1)*m+y;}
void add(int x,int y,int p,int q,ll w)
{
	if(x>n||x<1||p>n||p<1||y>m||y<1||q>m||q<1||s[x][y]=='#'||s[p][q]=='#') return ;
	int u=id(x,y),v=id(p,q);
	cnt++;
	a[cnt].to=v;
	a[cnt].val=w;
	a[cnt].nxt=head[u];
	head[u]=cnt;
}
void dijkstra()
{
	while(!que.empty()){
		int u=que.top().num;
		que.pop();
		if(vis[u]) continue;
		vis[u]=true;
		for(int i=head[u];i;i=a[i].nxt){
			int v=a[i].to;
			if(dis[v]>dis[u]+a[i].val){
				dis[v]=dis[u]+a[i].val;
				que.push((point){v,dis[v]});
			}
		}
	}
}
signed main()
{
	scanf("%lld%lld%lld%lld",&n,&m,&p,&q);
	memset(dis,9,sizeof dis);
	for(int i=1;i<=n;i++) cin>>s[i],s[i]='0'+s[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(s[i][j]=='.') dis[id(i,j)]=0,que.push((point){id(i,j),0});
			if(s[i][j]=='L'){
				add(i-1,j+1,i,j,p);
				add(i+1,j+1,i,j,p);
				add(i-1,j,i,j+1,p);
				add(i+1,j,i,j+1,p);
                
				add(i,j-1,i,j+1,q);
				add(i,j+2,i,j,q);
			}
			if(s[i][j]=='U'){
				add(i,j-1,i+1,j,p);
				add(i,j+1,i+1,j,p);
				add(i+1,j-1,i,j,p);
				add(i+1,j+1,i,j,p);
				
				add(i+2,j,i,j,q);
				add(i-1,j,i+1,j,q);
			}
		}
	}
	dijkstra();
	ll ans=1e18;
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m;j++){
//			printf("%d %lld\n",id(i,j),dis[id(i,j)]);
//		}
//		printf("\n");
//	}
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m;j++) printf("%lld ",dis[id(i,j)]);
//		printf("\n");
//	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(i!=n) ans=min(ans,dis[id(i,j)]+dis[id(i+1,j)]);
			if(j!=m) ans=min(ans,dis[id(i,j)]+dis[id(i,j+1)]);
			
		}
	}
	if(ans>=1e16) printf("-1\n");
	else printf("%lld\n",ans);
	return 0;
}
posted @ 2022-10-25 16:36  lxzy  阅读(23)  评论(0)    收藏  举报