洛谷 P1606 [USACO07FEB]荷叶塘Lilypad Pond(spfa+最短路计数) 题解

题目来源:

https://www.luogu.org/problemnew/show/P1606

题目描述:

 

题目描述

FJ has installed a beautiful pond for his cows' aesthetic enjoyment and exercise.

The rectangular pond has been partitioned into square cells of M rows and N columns (1 ≤ M ≤ 30; 1 ≤ N ≤ 30). Some of the cells have astonishingly sturdy lilypads; others have rocks; the remainder are just beautiful, cool, blue water.

Bessie is practicing her ballet moves by jumping from one lilypad to another and is currently located at one of the lilypads. She wants to travel to another lilypad in the pond by jumping from one lilypad to another.

Surprising only to the uninitiated, Bessie's jumps between lilypads always appear as a chess-knight's move: one move in one direction and then two more in the orthogonal direction (or perhaps two in one direction and then one in the orthogonal direction).

Farmer John is observing Bessie's ballet drill and realizes that sometimes she might not be able to jump to her destination lilypad because intermediary lilypads are missing.

Ever thrifty, he wants to place additional lilypads so she can complete her quest (perhaps quickly, perhaps by using a large number of intermediate lilypads). Of course, lilypads cannot be placed where rocks already intrude on a cell.

Help Farmer John determine the minimum number of additional lilypads he has to place, and in how many ways he can place that minimum number.

为了让奶牛们娱乐和锻炼,农夫约翰建造了一个美丽的池塘。这个长方形的池子被分成了M行N列个方格(1≤M,N≤30)。一些格子是坚固得令人惊讶的莲花,还有一些格子是岩石,其余的只是美丽、纯净、湛蓝的水。

贝西正在练习芭蕾舞,她站在一朵莲花上,想跳到另一朵莲花上去,她只能从一朵莲花跳到另一朵莲花上,既不能跳到水里,也不能跳到岩石上。

贝西的舞步很像象棋中的马步:每次总是先横向移动一格,再纵向移动两格,或先纵向移动两格,再横向移动一格。最多时,贝西会有八个移动方向可供选择。

约翰一直在观看贝西的芭蕾练习,发现她有时候不能跳到终点,因为中间缺了一些荷叶。于是他想要添加几朵莲花来帮助贝西完成任务。一贯节俭的约翰只想添加最少数量的莲花。当然,莲花不能放在石头上。

请帮助约翰确定必须要添加的莲花的最少数量,以及有多少种放置这些莲花的方法。

输入输出格式

输入格式:

【输入】

第一行:两个用空格分开的整数:M和N

第二行到M+1行:第i+1行有N个用空格分开的整数,描述了池塘第i行的状态:

0为水,1为莲花,2为岩石,3为贝西所在的起点,4为贝西想去的终点。

输出格式:

【输出】

第一行:一个整数,需要增加的最少莲花数;如果无解,输出-1。

第二行:放置这些莲花的方案数量,保证这个数字不会超过一个64位的有符号整数,

如果第一行是-1,不要输出第二行。

输入输出样例

输入样例#1: 复制

4 5
1 0 0 0 0
3 0 0 0 0
0 0 2 0 0
0 0 0 4 0

输出样例#1: 复制

2
3

说明

【样例说明】

池塘分成四行五列,贝西的起点在第二行第一列,想去的终点在第四行第四列,池

塘里一共有三朵莲花和一块石头。

最少需要两朵莲花,有三种方式可以放置,

页6 如下X所示:

10000 10X00 10X00

30X00 30000 3000X

00200 0X200 00200

0X040 00040 00040

解题思路:

       感觉这题的思路十分巧妙,第一个问是比较简单的,我们只要每个不是2的点,能到的点连一条边,如果那点是1,边权就是0,否则边权是1,然后跑一遍最短路就行,但是第二问就很难办了,因为本题是求放莲花的种数,不是最短路条数,比如如果不用莲花就能到达,可能有好几条最短路,可是答案应该是1,所以我们应该要把原图中的边权为0的边删去,我们可以在遇到已经是莲花的点,在递归的找这点能到的点,直到找到不是莲花的点,然后连一条边权为1的边,就相当于用了那朵莲花,那样答案就是最短路和最短路条数。。。。

代码:

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#define inf 0x3f3f3f3f
const int N =40;
const int M = 40*40;
using namespace std;
int n,m,map[N][N],S,T,dis[M];
long long ans[M];
bool vis[M],jl[N][N];
int head[200005],nxt[100010],ver[100010],edge[100010],tot;
int dir[8][2]={{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
void addedge(int x,int y,int z)
{
    ver[++tot]=y;
    edge[tot]=z;
    nxt[tot]=head[x];
    head[x]=tot;
}//邻接表存边
void add(int xy,int x,int y)
{
	jl[x][y]=1;
	for(int i=0;i<8;i++)
	{
		int xx=x+dir[i][0],yy=y+dir[i][1];
		if(xx<1||xx>n||yy<1||yy>m||map[xx][yy]==2||jl[xx][yy])continue;
		jl[xx][yy]=1;//标记以免重复 
		if(map[xx][yy]==1)add(xy,xx,yy);//是荷叶就继续建边; 
		else addedge(xy,(xx-1)*m+yy,1);
	}
}
queue<int>q;
int main()
{
	std::ios::sync_with_stdio(false);//怕超时关闭读入同步 
	memset(dis,inf,sizeof(dis));
	cin>>n>>m;
	tot=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		cin>>map[i][j];
		if(map[i][j]==3){
			S=(i-1)*m+j;
		}
		if(map[i][j]==4){
			T=(i-1)*m+j;
		}
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		if(map[i][j]!=1&&map[i][j]!=2){
			memset(jl,0,sizeof(jl));//每次清空标记 
			add((i-1)*m+j,i,j);		
		}
	}
	q.push(S);
	dis[S]=0;
	ans[S]=1;
	vis[S]=1;
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int i=head[now];i;i=nxt[i])
		{
			int v=ver[i],val=edge[i];
			if(dis[v]>dis[now]+val){
				dis[v]=dis[now]+val;
				ans[v]=ans[now];//最短路计数 
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
			else if(dis[v]==dis[now]+val){
				ans[v]+=ans[now];//最短路计数 
			}
		}
	}
	if(dis[T]==inf)cout<<"-1"<<endl;
	else cout<<dis[T]-1<<endl<<ans[T]<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40400202/article/details/81102182
今日推荐