[NOIP模拟][bzoj 2143][最短路]飞飞侠

题目描述:
题目链接: bzoj 2143 飞飞侠
飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个N×M的矩形方阵,每个格子代表一个街区。然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。我们设第i行第j列的弹射装置有Aij的费用和Bij的弹射能力。并规定有相邻边的格子间距离是1。那么,任何飞飞侠都只需要在(i,j)支付Aij的费用就可以任意选择弹到距离不超过Bij的位置了。如下图 (从红色街区交费以后可以跳到周围的任意蓝色街区。) 现在的问题很简单。有三个飞飞侠,分别叫做X,Y,Z。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你3个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。
Input
输入的第一行包含两个整数N和M,分别表示行数和列数。接下来是2个N×M的自然数矩阵,为Aij和Bij 最后一行六个数,分别代表X,Y,Z所在地的行号和列号。
Output
第一行输出一个字符X、Y或者Z。表示最优集合地点。第二行输出一个整数,表示最小费用。如果无法集合,只输出一行NO
Sample Input

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

Sample Output

Z
15

范围:
对于100%: 1 < = N, M < = 150; 0 < = Aij < = 10^9; 0 < = Bij < = 1000
题目分析:
朴素的想法是根据题目要求,如果一个点u可以弹射到另一个点v,就建一条单向边,u指向v,边权为弹射费用,然后作最短路。这种做法建的边的数量极大,甚至存不下,期望得分50分。优化: 直接根据原图特点作最短路,可以多的一些分。然后什么n棵线段树或者bitset优化建边什么的可以过,orz。
正解: 我们引入一个云端的概念。我们定义初始图的高度为0,对于每个点根据它的弹射能力h,在高度为h上建一个对应点,边权为弹射费用。对于云端h的一个点,建5条边,连向云端h-1上的5个点(四个方向上的点,加自己在云端h-1的对应点),边权为0。也就是说当你把一个点弹射到它对应的最大高度云端时,往下走是不消耗费用的。这就体现了你在某个点弹一次(体现在弹射到对应的最大高度云端点)就可以到达一些点(体现在云端上的点只能向下走)。这样做的好处是建的边不会太多,点数和边数同级,为O(NMH)(图中最大弹射能力)。再做三遍dijkstra+堆优化,就可以了。最坏时间复杂度O( N 3 l o g N N^3logN ),空间复杂度O( N 3 N^3 )。
PS:
1、实际操作上不用建边,dijkstra时根据特点走就行。
2、考试数据要比bzoj上的数据强一些儿,所以那些什么直接原图dijkstra在bzoj能过的,是不能满分的。
附代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<algorithm>
#include<queue>
using namespace std;

const long long INF=1e18;
const int fx[5]={0,1,0,-1,0};
const int fy[5]={0,0,1,0,-1};
int n,m,H,A[210][210],B[210][210];
long long dis[210][210][420],w[5],ans,len[5][5];
bool flag1,flag2;
char pe;
struct node{
	int x,y,h;
	friend inline bool operator < (const node &a,const node &b)
	{
		return a.h<b.h;
	} 
}src[5];
priority_queue<pair<long long,node> >q;

int readint()
{
	char ch;int i=0,f=1;
	for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
	if(ch=='-') {ch=getchar();f=-1;}
	for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
	return i*f;
}

void dijkstra(int sta,int des1,int des2)
{
	flag1=false;flag2=false;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<=H;k++)
				dis[i][j][k]=INF;
	while(!q.empty()) q.pop();
	int x=src[sta].x;int y=src[sta].y;
	dis[x][y][A[x][y]]=B[x][y];
	q.push(make_pair(-B[x][y],src[sta]));
	while(!q.empty())
	{
		node a=q.top().second;
		long long d=dis[a.x][a.y][a.h];
		q.pop();
		if(a.x==src[des1].x&&a.y==src[des1].y&&a.h==0) {flag1=true;len[sta][des1]=d;}//如果出栈了目标点说明就是最短路,一定要判高度为0,因为云端上的更新不一定最优,但到地面相互比较过,就是最优的
		if(a.x==src[des2].x&&a.y==src[des2].y&&a.h==0) {flag2=true;len[sta][des2]=d;}
		if(flag1==true&&flag2==true) return;//两个都找到就不用继续了
		if(a.h>0)
		{
			for(int i=0;i<=4;i++)
			{
				node b;
				b.x=a.x+fx[i];
				b.y=a.y+fy[i];
				b.h=a.h-1;
				if(b.x>=1&&b.x<=n&&b.y>=1&&b.y<=m&&dis[b.x][b.y][b.h]>d)
				{
					dis[b.x][b.y][b.h]=d;
					q.push(make_pair(-d,b));
				}
			}
		}
		else//高度为0就再从当前点弹上去
		{
			a.h=A[a.x][a.y];
			if(dis[a.x][a.y][a.h]>d+B[a.x][a.y])
			{
				dis[a.x][a.y][a.h]=d+B[a.x][a.y];
				q.push(make_pair(-dis[a.x][a.y][a.h],a));
			}
		}
	}
}

int main()
{
	//freopen("friend.in","r",stdin);
	//freopen("friend3.out","w",stdout);
	
	int x,y;
	n=readint();m=readint();
	H=n+m-2;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			A[i][j]=min(readint(),H);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			B[i][j]=readint();
	for(int i=1;i<=3;i++)
	{
		src[i].x=readint();
		src[i].y=readint();
		src[i].h=A[src[i].x][src[i].y];
	}
	for(int i=1;i<=3;i++)
		for(int j=1;j<=3;j++)
			len[i][j]=INF;
	dijkstra(1,2,3);w[2]+=len[1][2];w[3]+=len[1][3];
	dijkstra(2,1,3);w[1]+=len[2][1];w[3]+=len[2][3];
	dijkstra(3,2,1);w[2]+=len[3][2];w[1]+=len[3][1];
	ans=INF;
	if(w[1]<ans) {ans=w[1];pe='X';}
	if(w[2]<ans) {ans=w[2];pe='Y';}
	if(w[3]<ans) {ans=w[3];pe='Z';}
	if(ans==INF) printf("NO");
	else printf("%c\n%lld",pe,ans);
	
	return 0;
}
发布了99 篇原创文章 · 获赞 8 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qianguch/article/details/78410814