USACO CONTEST FEB07 白银莲花池 silvlily 分析

白银莲花池
(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;
}



 

 

转载注明出处:http://blog.csdn.net/u011400953

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 题目描述 有N头奶牛,它们在M个牛棚之间相互转移。每个牛棚里有一些奶牛,每分钟可以容纳一头奶牛。一头奶牛从一个牛棚走到另一个牛棚需要一分钟的时间。现在,这些奶牛要开一个牛派对,它们要在同一时间到达同一个牛棚,所以它们需要在某个牛棚等待一段时间。你需要计算最小的等待时间,使得所有奶牛都能够在同一时间到达同一个牛棚。 输入格式 第一行包含三个整数N,M,X。 接下来M行,每行包含三个整数a,b,t,表示牛棚a和牛棚b之间有一条双向边,需要t分钟才能通过。 输出格式 输出一个整数,表示最小等待时间。 数据范围 1≤N≤500 1≤M≤10000 1≤X≤N 1≤a,b≤N 1≤t≤1000 输入样例#1 3 3 1 1 2 5 2 3 5 1 3 10 输出样例#1 5 输入样例#2 4 5 4 1 2 10 2 3 10 3 4 10 4 1 10 1 3 20 输出样例#2 30 算法1 (最短路) $O(N^3)$ Dijkstra算法 Dijkstra(迪杰斯特拉)算法是由荷兰计算机科学家狄克斯特拉于1956年发明的,因此又叫狄克斯特拉算法。 Dijkstra算法是一种贪心算法,用于求解一个节点到其他所有节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。 具体做法是:设立一个数组dis来保存源点到各个顶点的最短距离和一个数组book[i]来记录一个顶点是否已经在队列中。 初始时,原点s的路径权重被赋为0 (dis[s] = 0)。若对于顶点s存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,所有顶点并不属于任何已知最短路径所包含的顶点集合,因此都被标记为未知最短路径长度。当算法结束时,dis[v]中存储的便是源点s到顶点v的最短路径,或者如果从s无法到达v,则值为INF。 Dijkstra算法流程: 算法流程: 1. 将所有顶点分为两部分:已知最短路的顶点集合P和未知最短路的顶点集合Q。 2. 初始时,顶点集合P中只有源点s一个元素,以源点s为起点向外扩展。 3. 每次从顶点集合Q中选取一个顶点u(u的dist最小),并加入到顶点集合P中,同时以u为中心进行扩展。 4. 重复步骤3,直到顶点集合Q为空或者终点被加入到顶点集合P中。 5. 算法结束,最短路径保存在dis数组中。 时间复杂度 Dijkstra算法的时间复杂度为O(N^2)。由于N较小,因此可以通过本题。 参考文献 Dijkstra算法讲解 C++ 代码 算法2 (最短路) $O(N^2)$ Floyd算法 Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。 Floyd算法的基本思想 设G=(V,E)是一个带权有向图,其邻接矩阵为W。V={v1,v2,……,vn},W[1:n,1:n],则该图的Floyd算法可描述如下: 时间复杂度 Floyd算法的时间复杂度为O(N^3)。由于N较小,因此可以通过本题。 参考文献 Floyd算法讲解 C++ 代码 算法3 (最短路) $O(N^2)$ Bellman-Ford算法 Bellman-Ford算法是一种单源最短路径算法,可以处理负权边,但不能处理负权回路。 Bellman-Ford算法的基本思想 对于图中的任意一条边(u, v),Bellman-Ford算法会对每一条边进行一次松弛操作(Relax),并且这些操作是按照顺序进行的:当算法进行第i次松弛操作时,它只会改变长度为i+1的路径上的顶点的值。因此,当算法执行完第n-1次松弛操作后,路径长度最长不超过n-1,此时所有最短路径都已经求出。 时间复杂度 Bellman-Ford算法的时间复杂度为O(N*M)。由于N和M的范围较小,因此可以通过本题。 参考文献 Bellman-Ford算法讲解 C++ 代码 ### 回答2: Usaco 2007 Feb的问题是关于Cow Party的。这个问题中,农夫约翰有N头奶牛,它们之间通过一些路径相互连接,并且每个路径都有一个长度。约翰想要在某个时间将它的所有奶牛聚集在一起举办一个派对,现在他想知道所有奶牛从各自的位置到达聚会地点所需的最短时间。 为了解决这个问题,我们可以使用Dijkstra算法。我们首先需要创建一个节点集合,包含所有的奶牛和派对地点,并且初始化每个节点的最短时间为无穷大。接下来,我们选取一个起点节点--聚会地点,并将它的最短时间设置为0。然后我们开始遍历所有的节点,每次选择一个最短时间未确定的节点,并更新它的邻居节点的最短时间。我们重复这个过程,直到所有节点的最短时间都确定。 在更新节点的最短时间时,我们需要根据节点之间的路径长度来更新。我们检查从当前节点到邻居节点的路径长度加上当前节点的最短时间是否小于邻居节点目前的最短时间。如果是,则更新邻居节点的最短时间为新的最短时间。 最后,我们可以得到所有奶牛到达聚会地点所需的最短时间。我们找到所有奶牛起始位置的最长最短时间,即为我们的答案。 通过使用Dijkstra算法,我们可以解决这个问题并得到最优解。因此,Usaco 2007 Feb的Cow Party问题可以通过这种方法解决。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值