[NOIP模拟题]chess(最短路计数)

题意

有一个(m*n)的棋盘((n,mleq 50)),每个各自上有数字,一个马从起始位置出发跳向目标位置,走到0号格子会花费1代价,走到1号格子或者目标位置不花费代价,2号格子不能走,求(S)(T)的最小代价及方案数(注意:两种方案不同当且仅当路径上的至少有一个不同的0号格子

思路

下面的论述都不考虑2号格子

对于最小代价,很容易想到建图跑最短路:对于每个点向八个方向连边,如果是0号边权为1,1号边权为0,跑一遍dijkstra即可

对于方案数,如果两种方案不同的条件换成“两条路径上有一个点不同即可”且所有边权为1,这道题就是最短路计数

然而两条路径不同只取决于0号格子,也就是说,从一个0号格子到另外一个0号格子,不需要管中间怎么走,也就是说其实1号格子并没有任何作用,如果两个0号点之间全是1号点,我们可以跳过这些1号点使得0号点直接相连(当然如果两个0号点之间还有0号点就不用管了)

所以,从每一个0号点出发(起点终点也要算0号点),dfs一遍找出所有的中间只经过1号格子即可到达的0号点,两者直接连边,用vis标记使得一个点在一次dfs中只出现一次,避免重边或者反复横跳

这样做之后,整张图边权全为1且只剩下0号格子连有边,当作最短路计数做即可,注意由于目标格子不要代价,最终的最短路长度要-1(这样做就已经可以求出最小代价了,可以不用特意跑一次dijkstra)

Code:

#include<bits/stdc++.h>
#define N 2605
using namespace std;
typedef long long ll;
int n,m,S,T;
int a[55][55];
int dis[N],kkk;
int dox[10]={-1,-2,-2,-1,1,2,2,1},doy[10]={-2,-1,1,2,2,1,-1,-2};
bool vis[N];
vector<int> son[N];
ll ans[N];

struct Edge
{
	int next,to,dis;
}edge[N<<4];int head[N],cnt=1;
void add_edge(int from,int to,int dis)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	edge[cnt].dis=dis;
	head[from]=cnt;
}
template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
int get_id(int x,int y) 
{
	if(x<1||x>n||y<1||y>m) return -1;
	return (x-1)*50+y;
}
void dfs(int root,int rt)
{
	vis[rt]=1;
	if(rt==S&&root!=rt) return;
	if(a[(rt-1)/50+1][(rt-1)%50+1]%4==0&&rt!=root) {son[root].push_back(rt);return;}
	
	for(int i=head[rt];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!vis[v]) dfs(root,v);
	}
}
int main()
{
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	read(n);read(m);
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			read(a[i][j]);
			if(a[i][j]==3) S=get_id(i,j);
			if(a[i][j]==4) T=get_id(i,j);
		}
	}
	for(int i=1;i<=n;++i)//建图 
	{
		for(int j=1;j<=m;++j)
		{
			if(a[i][j]==2) continue;//可有可无 
			int now=get_id(i,j);
			for(int k=0;k<8;++k)
			{
				int dx=i+dox[k],dy=j+doy[k];
				int v=get_id(dx,dy);
				if(v==-1||a[dx][dy]==2) continue;//出界或者踩到友军 
				if(a[dx][dy]==0||a[dx][dy]==3) add_edge(now,v,1);
				else add_edge(now,v,0);
			}
		}
	}
	for(int i=1;i<=n;++i)
	for(int j=1;j<=m;++j)
	{
		if(a[i][j]==0||a[i][j]==3) 
		{
			memset(vis,0,sizeof(vis));
			dfs(get_id(i,j),get_id(i,j));
		}
	}
	memset(dis,-1,sizeof(dis));
	queue<int> q;
	ans[S]=1; q.push(S); dis[S]=0;
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(int i=0;i<(int)son[u].size();++i)
		{
			int v=son[u][i];
			if(dis[v]==-1)
			{
				if(v!=T) dis[v]=dis[u]+1,q.push(v);//此处也可以不特判,最后dis[T]-1即可
				else dis[v]=dis[u];
			}
			if(dis[v]==dis[u]+1||(dis[v]==dis[u]&&v==T)) ans[v]+=ans[u];
		}
	}
	if(dis[T]==-1) cout<<-1<<endl;
	else cout<<dis[T]<<endl<<ans[T]<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11396820.html