白银莲花池
(silvlily.pas/c/c++)
【问题描述】
为了让同学们学习和锻炼,学校建造了一个美丽的池塘。这个长方形的池子被分成了M行N列个方格(1 ≤ M, N ≤ 30)。一些格子是坚固得令人惊讶的莲花,还有一些格子是岩石,其余的只是美丽、纯净、湛蓝的水。
贝西正在练习芭蕾舞,她站在一朵莲花上,想跳到另一朵莲花上去,她只能从一朵莲花跳到另一朵莲花上,既不能跳到水里,也不能跳到岩石上。
贝西的舞步很像象棋中的马步:每次总是先横向移动一格,再纵向移动两格,或先纵向移动两格,再横向移动一格。最多时,贝西会有八个移动方向可供选择。 同学们一直在观看贝西的芭蕾练习,发现她有时候不能跳到终点,因为中间缺了一些荷叶。于是他们想要添加几朵莲花来帮助贝西完成任务。同学们只想添加最少数量的莲花。当然,莲花不能放在石头上。
请确定必须要添加的莲花的最少数量。在添加莲花最少的基础上,确定贝西从起点跳到目标需要的最少步数。最后,确定满足添加的莲花数量最少时,步数最少的路径条数。
【输入格式】
第一行:两个用空格分开的整数:M和N
第二行到M + 1行:第i + 1行有N个用空格分开的整数,描述了池塘第i行的状态:0 为水,1 为莲花,2 为岩石,3 为YJC所在的起点,4 为贝西想去的终点。
【输出格式】
第一行:一个整数:需要添加的莲花的最少数目;如果无解,则输出-1
第二行:一个整数:在添加莲花最少的基础上,YJC从起点跳到终点需要的最少步数;如果第一行是-1,不输出这行
第三行:一个整数:在添加莲花最少的基础上,步数等于第二行输出的路径条数;如果第一行是-1,不输出这行
【样例】
4 8
0 0 0 1 0 0 0 0
0 0 0 0 0 2 0 1
0 0 0 0 0 4 0 0
3 0 0 0 0 0 1 0
2
6
2
【样例说明】
(最少要加两朵莲花,位置如 x 所示:
0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0
0 x 0 0 0 2 0 1 0 0 0 0 0 2 0 1
0 0 0 0 x 4 0 0 0 0 x 0 x 4 0 0
3 0 0 0 0 0 1 0 3 0 0 0 0 0 1 0
贝西至少要跳六步,两种不同的跳法如下:
0 0 0 C 0 0 0 0 0 0 0 C 0 0 0 0
0 B 0 0 0 2 0 F 0 0 0 0 0 2 0 F
0 0 0 0 D G 0 0 0 0 B 0 D G 0 0
A 0 0 0 0 0 E 0 A 0 0 0 0 0 E 0
【分析】:
令dist[x][y][z]表示(x,y)的最优莲花数位z,走了这么多步。用类似spfa的方法广搜:从起始点搜起,用spfa中的松弛操作<现在点的最优步数+1<到达点的步数,更新到达点的最优步数>优化,最后在终点的dist中找z的最小值且存在步数。再用这个dist数组从终点倒退回起点,用final[x][y][z]记录到(x,y)点有多少条莲花数为z的路,倒退条件当且仅当现在点的dist-1=到达点的dist,最后起点的final值即为路径数
【代码】:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#define MAX 31
#define MAXN 901
#define IMAX 2147483647
struct point{int x,y,num;};
const int dx[]={1,1,-1,-1,2,2,-2,-2},dy[]={2,-2,2,-2,-1,1,-1,1};
int M,N,startx,starty,endx,endy;
int map[MAX][MAX],ansstep=IMAX,ansnum=IMAX;
int dist[MAX][MAX][MAXN];
long long final[MAX][MAX][MAXN],ansroad=0;
bool vis[MAX][MAX][MAXN];
void Q1()
{
queue<point> Q;
point use1;
for(int i=1;i<=M;i++)
for(int j=1;j<=N;j++)
for(int k=0;k<=N*M/2;k++)
dist[i][j][k]=IMAX;
use1.x=startx,use1.y=starty,use1.num=0;
Q.push(use1);
dist[use1.x][use1.y][0]=0;
while(!Q.empty())
{
point now=Q.front();
Q.pop();
for(int i=0;i<8;i++)
if(0<now.x+dx[i] && now.x+dx[i]<=M && 0<now.y+dy[i] && now.y+dy[i]<=N)
{
if(map[now.x+dx[i]][now.y+dy[i]]!=2 && dist[now.x][now.y][now.num]+1<dist[now.x+dx[i]][now.y+dy[i]][now.num+(map[now.x+dx[i]][now.y+dy[i]]==0 ? 1 : 0)])
{
dist[now.x+dx[i]][now.y+dy[i]][now.num+(map[now.x+dx[i]][now.y+dy[i]]==0 ? 1 : 0)]=dist[now.x][now.y][now.num]+1;
if(!vis[now.x+dx[i]][now.y+dy[i]][now.num+(map[now.x+dx[i]][now.y+dy[i]]==0 ? 1 : 0)])
{
point use;
vis[now.x+dx[i]][now.y+dy[i]][now.num+(map[now.x+dx[i]][now.y+dy[i]]==0 ? 1 : 0)]=true;
use.x=now.x+dx[i];
use.y=now.y+dy[i];
use.num=now.num+(map[now.x+dx[i]][now.y+dy[i]]==0 ? 1 : 0);
Q.push(use);
}
}
}
}
for(int i=0;i<=N*M/2;i++)
if(dist[endx][endy][i]!=IMAX)
{
ansnum=i;
ansstep=dist[endx][endy][i];
break;
}
}
void Q2()
{
queue<point> Q;
point use1;
memset(vis,false,sizeof(vis));
use1.x=endx,use1.y=endy,use1.num=ansnum;
Q.push(use1);
final[use1.x][use1.y][ansnum]=1;
while(!Q.empty())
{
point now=Q.front();
Q.pop();
if(!now.num && !map[now.x][now.y]) continue;
for(int i=0;i<8;i++)
if(0<now.x+dx[i] && now.x+dx[i]<=M && 0<now.y+dy[i] && now.y+dy[i]<=N)
{
if(map[now.x+dx[i]][now.y+dy[i]]!=2 && dist[now.x][now.y][now.num]-1==dist[now.x+dx[i]][now.y+dy[i]][now.num-(map[now.x][now.y]==0 ? 1 : 0)])
{
final[now.x+dx[i]][now.y+dy[i]][now.num-(map[now.x][now.y]==0 ? 1 : 0)]+=final[now.x][now.y][now.num];
if(!vis[now.x+dx[i]][now.y+dy[i]][now.num-(map[now.x][now.y]==0 ? 1 : 0)])
{
point use;
vis[now.x+dx[i]][now.y+dy[i]][now.num-(map[now.x][now.y]==0 ? 1 : 0)]=true;
use.x=now.x+dx[i];
use.y=now.y+dy[i];
use.num=now.num-(map[now.x][now.y]==0 ? 1 : 0);
Q.push(use);
}
}
}
}
ansroad=final[startx][starty][0];
}
int main()
{
freopen("silvlily.in","r",stdin);
freopen("silvlily.out","w",stdout);
scanf("%d%d",&M,&N);
for(int i=1;i<=M;i++)
for(int j=1;j<=N;j++)
{
scanf("%d",&map[i][j]);
if(map[i][j]==3) startx=i,starty=j,map[i][j]=0;
if(map[i][j]==4) endx=i,endy=j,map[i][j]=0;
}
Q1();
if(ansnum==IMAX) {printf("-1\n");return 0;}
Q2();
printf("%d\n%d\n%lld\n",ansnum-1,ansstep,ansroad);
//system("pause");
return 0;
}
【官方题解】:
USACO FEB07 Problem 'silvlily' Analysis
by Richard Ho
We can treat this as a shortest path problem. At each square we keep track of how many lilypads we have to place to get there and how many jumps it took. We first want to minimize the lilypads and then minimize the jumps (moving onto a lilypad only takes one jump), while moving onto a blank square takes one lilypad + one jump.
We use BFS to update this information as we traverse the various routes. If a jump brings us to a square taking the same number of lilypads and jumps as it already says on the square, we can add the number of ways to that square. If we did better, we replace the number of ways to that square and add to queue. If we did worse, we don't update it. Below is code that matches this description.
【官方代码】:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
FILE *in = fopen("silvlily.in", "r"), *out = fopen("silvlily.out", "w");
int m,n,d[50][50];
struct p {int x, y;};
class node
{
public:
int bestlily;
int bestjump;
long long nways;
node() {bestlily=-1; bestjump=-1; nways=0;}
};
node grid[50][50];
bool inq[50][50];
#define QSIZE 10000
p q[QSIZE];
int qstart,qend;
void enq(int x, int y)
{
if (!inq[x][y])
{
inq[x][y]=true;
q[qend].x=x;
q[qend].y=y;
qend++; qend%=QSIZE;
}
}
void updateq(int x, int y, int bl, int bj, long long pways)
{
if (grid[x][y].bestlily==-1 || bl<grid[x][y].bestlily)
{
grid[x][y].bestlily=bl;
grid[x][y].bestjump=bj;
grid[x][y].nways=pways;
enq(x,y); return;
}
if (grid[x][y].bestlily==bl && bj<grid[x][y].bestjump)
{
grid[x][y].bestlily=bl;
grid[x][y].bestjump=bj;
grid[x][y].nways=pways;
enq(x,y); return;
}
if (grid[x][y].bestlily==bl && bj==grid[x][y].bestjump)
{
grid[x][y].nways+=pways;
return;
}
}
int dirs[8][2]={{1,2},{2,1},{-1,2},{-2,1},{-1,-2},{-2,-1},{1,-2},{2,-1}};
void processqhead(void)
{
int x=q[qstart].x,y=q[qstart].y,nx,ny;
for (int i=0;i<8;i++)
{
nx=x+dirs[i][0];
ny=y+dirs[i][1];
if (nx>=0 && ny>=0 && nx<m && ny<n)
{
if (d[nx][ny]!=2)
updateq(nx,ny,grid[x][y].bestlily+(d[nx][ny]==1?0:1),grid[x][y].bestjump+1,grid[x][y].nways);
}
}
inq[x][y]=false;
qstart++; qstart%=QSIZE;
}
int main(void)
{
int i,j;
p a,b;
fscanf(in,"%i %i",&m,&n);
for (i=0;i<m;i++) for (j=0;j<n;j++) fscanf(in,"%i",&d[i][j]);
for (i=0;i<m;i++) for (j=0;j<n;j++)
{
if (d[i][j]==3) {d[i][j]=1; a.x=i;a.y=j;}
if (d[i][j]==4) {d[i][j]=1; b.x=i;b.y=j;}
inq[i][j]=false;
}
qstart=0; qend=0;
updateq(a.x,a.y,0,0,1);
while (qstart!=qend)
{
processqhead();
}
if (grid[b.x][b.y].bestlily==-1) fprintf(out,"-1\n");
else
fprintf(out,"%i\n%i\n%lld\n",grid[b.x][b.y].bestlily,grid[b.x][b.y].bestjump,grid[b.x][b.y].nways);
fclose(in); fclose(out); return 0;
}