Vova Escapes the Matrix

做的时候就差如何得出一个点到两个不同的出口的最短路和次短路了啊

分类讨论

如果图不能到达出口,那么可以把所有'.'都填了

如果图只能达到一个出口,那么就是所有'.'的个数减去起点到这个出口的最短路

如果图可以到达两个及以上出口,考虑填满陷阱之后,图长成什么样子:此时一定刚好还剩下两个可到达的出口,所以我们可以找到两条路;如果这两条路分叉了之后又汇聚到一个点,显然是不优的(可以把分叉的路变成一条路,从而将另一条路填上陷阱),于是这两条路一定是先一起走,然后分叉一直到两个终点,于是我们可以得出算法,先枚举分叉点,算出起点到分叉点的距离,再算分叉点到两个不同的出口的最短路和次短路即可(如果分叉点到两个不同的出口的最短路和次短路仍然有重叠,无所谓,因为我们会枚举所有点,不会遗漏最优答案)

最主要的就是如何得出一个点到两个不同的出口的最短路和次短路,此时要记录路径长度以及起点,具体见下面的代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010;
int n,m;
pair<int,int> st;
queue<pair<int,int> > q;
queue<pair<pair<int,int>,int> > Q;
vector<pair<int,int> > e;
char s[N][N];
bool vis[N][N],mark[N][N][2];
int f[N][N][2],dist[N][N];
pair<int,int> g[N][N][2];
int dx[5]={-1,0,1,0},dy[5]={0,1,0,-1};
bool check(int x,int y)
{
	if((x==1||x==n||y==1||y==m)&&s[x][y-1]!='#') return 1;
	return 0;
}
bool valid(pair<int,int> x)
{
    if(x.first<1||x.first>n||x.second<1||x.second>m||s[x.first][x.second-1]=='#') return 0;
    return 1;
}
int bfs()
{
	int cnt=0;
	q.push(st);
	while(!q.empty())
	{
		pair<int,int> u=q.front();
		q.pop();
		if(vis[u.first][u.second]) continue;
		vis[u.first][u.second]=1;
		if(check(u.first,u.second)) cnt++;
		for(int i=0;i<4;i++)
		{
			pair<int,int> v;
			v.first=u.first+dx[i],v.second=u.second+dy[i];
			if(valid(v)) q.push(v);
		}
	}
	if(cnt<=1) return cnt;
	else return 2;
}
pair<int,int> work1()
{
    pair<int,int> res;
	q.push(st);
	dist[st.first][st.second]=0;
	while(!q.empty())
	{
		pair<int,int> u=q.front();
		q.pop();
		if(vis[u.first][u.second]) continue;
		vis[u.first][u.second]=1;
		if(check(u.first,u.second)&&dist[u.first][u.second]<1000000000) 
		res=u;
		for(int i=0;i<4;i++)
		{
			pair<int,int> v;
			v.first=u.first+dx[i],v.second=u.second+dy[i];
			if(valid(v)) 
			{
				q.push(v);
				dist[v.first][v.second]=min(dist[u.first][u.second]+1,dist[v.first][v.second]);
			}
		}
	}
	return res;
}
void work2()
{
	for(int i=0;i<e.size();i++) 
	{
		Q.push(make_pair(e[i],0));
		f[e[i].first][e[i].second][0]=0;//f表示最短路和次短路距离 
		g[e[i].first][e[i].second][0]=e[i];//g 表示最短路和次短路起点
	}
	while(!Q.empty())
	{
		pair<pair<int,int>,int> u=Q.front();
		Q.pop();
		if(mark[u.first.first][u.first.second][u.second]) continue;
		mark[u.first.first][u.first.second][u.second]=1;
		for(int i=0;i<4;i++)
		{
			pair<pair<int,int>,int> v;
			v.first.first=u.first.first+dx[i];
			v.first.second=u.first.second+dy[i];
			if(!valid(v.first)) continue;
			if(u.second==0)
			{
				if(f[v.first.first][v.first.second][0]>f[u.first.first][u.first.second][0]+1)
				{
					/*f[v.first.first][v.first.second][1]=f[v.first.first][v.first.second][0];
					g[v.first.first][v.first.second][1]=g[v.first.first][v.first.second][0];*/
					//在边长为1的情况下,上述情况是不可能发生的,所以可以注释掉 
					f[v.first.first][v.first.second][0]=f[u.first.first][u.first.second][0]+1;
					g[v.first.first][v.first.second][0]=g[u.first.first][u.first.second][0];
					Q.push(make_pair(v.first,0));
					//Q.push(make_pair(v.first,1));
				}
				else if(f[v.first.first][v.first.second][1]>f[u.first.first][u.first.second][0]+1&&g[v.first.first][v.first.second][0]!=g[u.first.first][u.first.second][0])
				{
					f[v.first.first][v.first.second][1]=f[u.first.first][u.first.second][0]+1;
					g[v.first.first][v.first.second][1]=g[u.first.first][u.first.second][0];
					Q.push(make_pair(v.first,1));
				}
			} 
			else if(f[v.first.first][v.first.second][1]>f[u.first.first][u.first.second][1]+1&&g[v.first.first][v.first.second][0]!=g[u.first.first][u.first.second][1])
			{
				f[v.first.first][v.first.second][1]=f[u.first.first][u.first.second][1]+1;
				g[v.first.first][v.first.second][1]=g[u.first.first][u.first.second][1];
				Q.push(make_pair(v.first,1));
			}
		}
	}
}
int main() 
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		e.clear();
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s[i]);
			for(int j=0;j<m;j++)
			{
				if(s[i][j]=='V') st.first=i,st.second=j+1;
				if(check(i,j+1)) e.push_back(make_pair(i,j+1));
			}
		}
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		vis[i][j]=0;
		int type=bfs(),ans=0;
		if(!type)
		{
			for(int i=1;i<=n;i++)
			for(int j=0;j<m;j++)
			if(s[i][j]=='.') ans++;
			printf("%d\n",ans);
		} 
		else if(type==1)
		{
			for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
			    vis[i][j]=0;
			    dist[i][j]=0x3f3f3f3f;
			}
			pair<int,int> ed=work1();
			for(int i=1;i<=n;i++)
			for(int j=0;j<m;j++)
			if(s[i][j]!='#') ans++;
			printf("%d\n",ans-dist[ed.first][ed.second]-1);
		}
		else
		{
			for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				vis[i][j]=mark[i][j][0]=mark[i][j][1]=0;
				//vis用于work1的访问标记
				//mark用于work2的访问标记 
				dist[i][j]=0x3f3f3f3f;
				for(int k=0;k<=1;k++)
				f[i][j][k]=0x3f3f3f3f,g[i][j][k]=make_pair(0,0);
			}
			work2();//得出一个点到两个不同的出口的最短路和次短路 
			work1();//得出起点到某一点的最短路 
			for(int i=1;i<=n;i++)
			for(int j=0;j<m;j++)
			if(s[i][j]!='#') ans++;
			int Min=0x7fffffff;
			for(int i=1;i<=n;i++)
			for(int j=0;j<m;j++)
			if(s[i][j]!='#'&&dist[i][j+1]<0x3f3f3f3f&&f[i][j+1][0]<0x3f3f3f3f&&f[i][j+1][1]<0x3f3f3f3f) Min=min(Min,dist[i][j+1]+f[i][j+1][0]+f[i][j+1][1]);
			printf("%d\n",ans-Min-1);
		}
	}
  	return 0;
}
posted @ 2024-07-21 14:08  最爱丁珰  阅读(3)  评论(0编辑  收藏  举报