HDU2482 - Transit search (Floyd)

链接 HDU2482

【题意】给定一张地图,每次双击可以把放大两倍,地图分成4个区域,左上角坐标为(0,0),给出经过8次操作后的起点和终点坐标;然后再给出m(m <= 5000)个车站的大坐标,以及n(n <= 100)条公交车路线,求出从起点到终点的最短换乘车的次数。

注意:如果起点和终点距离不到2000直接输出"walk there";必须从距离起点小于1000的车站开始坐车到距离终点小于1000的车站才行;如果没有路线可到达终点,输出"take a taxi";否则输出最少换车的数量。

【分析】首先要把给出的起点和终点坐标还原成大的坐标(即没有缩小时的坐标,因为该下面的车站坐标都是这个);

但是这个坐标怎么还原呢,很容易看出,从5120开始,如果在1号区域y轴需要加上,2号区域只加x轴,3号区域都加,因为每次放大都是两倍,所以比例尺每次除以2就可以了;

然后就是求起点到终点的最短路径,把每次换车看成边的权,因为最多只有100条线路,把每条线路看成边的顶点,每个车站分别映射到每条路线(可以映射多个),当一个车站同时在多个线路时,把所有这些线路连边即可,然后用floyd求出最短路,再枚举起点和终点取最小值就是答案。

这样复杂度主要来自建图的 N*M*K = 10^7

【AC CODE】78ms

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <vector>
using namespace std;
#define MAXM 5010
#define MAXN 105
#define INF 0x3f3f3f3f
#define min(a,b) (a<b?a:b)
map<string,int> num;
struct NODE{
	int len, r[35];
}p[MAXN];
int x[MAXM], y[MAXM], dp[MAXN][MAXN];
bool vis[MAXN];
vector<int> st,ed,start,endth;
vector<int> h[MAXM];//每个车站所在的线路

inline double dis(int a, int b){return sqrt((double)(x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));}
void get_xy(char *s, int& x, int& y)
{
	int base = 5120;
	for(int i = 0; i < 8; i++)
	{
		if('1' == s[i]) y += base;
		else if('2' == s[i]) x += base;
		else if('3' == s[i]) x += base, y += base;
		base >>= 1;
	}
}
int main()
{
#ifdef SHY
	freopen("e:\\1.txt","r",stdin);
#endif
	int t;
	scanf("%d%*c", &t);
	while(t--)
	{
		int sx, sy, ex, ey, n, m;
		char ch[30];
		num.clear();
		scanf("%s %d %d%*c", ch, &sx, &sy);
		get_xy(ch,sx,sy);
		scanf("%s %d %d%*c", ch, &ex, &ey);
		get_xy(ch,ex,ey);
		scanf("%d%*c", &m);
		for(int i = 0; i < m; i++)
		{
			scanf("%s %d %d%*c", ch, &x[i], &y[i]);
			num[ch] = i;
		}
		x[m] = sx, y[m] = sy, x[m+1] = ex, y[m+1] = ey;
		scanf("%d%*c", &n);
		for(int i = 0; i < n; i++)
		{
			scanf("%d%*c", &p[i].len);
			for(int j = 0; j < p[i].len; j++)
			{
				scanf("%s%*c", ch);
				p[i].r[j] = num[ch];
			}
		}
		if(dis(m,m+1) <= 2000.0)
		{
			puts("walk there");
			continue;
		}
		st.clear();
		ed.clear();
		for(int i = 0; i < m; i++)//找到所有起点和终点站
		{
			if(dis(i,m) <= 1000.0)
				st.push_back(i);
			if(dis(i,m+1) <= 1000.0)
				ed.push_back(i);
		}
		if(0 == st.size() || 0 == ed.size())
		{
			puts("take a taxi");
			continue;
		}
		for(int i = 0; i < m; i++) h[i].clear();
		memset(dp,0x3f,sizeof(dp));
		for(int i = 0; i < n; i++)//把线路看成点建图
		{
			for(int j = 0; j < p[i].len; j++)
			{
				int a = p[i].r[j];
				if(h[a].size() > 0)
				{
					for(int k = 0; k < h[a].size(); k++)
						dp[i][h[a][k]] = dp[h[a][k]][i] = 1;
				}
				h[a].push_back(i);
			}
		}
		for(int i = 0; i < n; i++) dp[i][i] = 0;
		for(int k = 0; k < n; k++)//floyd
		{
			for(int i = 0; i < n; i++)
			{
				for(int j = 0; j < n; j++)
					dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]);
			}
		}
		int ans = INF;
		start.clear();
		endth.clear();
		memset(vis,0,sizeof(vis));
		for(int i = 0; i < st.size(); i++)
		{
			int a = st[i];
			for(int j = 0; j < h[a].size(); j++)
			{
				if(!vis[h[a][j]])
				{
					vis[h[a][j]] = true;
					start.push_back(h[a][j]);
				}
			}
		}
		memset(vis,0,sizeof(vis));
		for(int i = 0; i < ed.size(); i++)
		{
			int a = ed[i];
			for(int j = 0; j < h[a].size(); j++)
			{
				if(!vis[h[a][j]])
				{
					vis[h[a][j]] = true;
					endth.push_back(h[a][j]);
				}
			}
		}
		for(int i = 0; i < start.size(); i++)
		{
			for(int j = 0; j < endth.size(); j++)
				ans = min(ans,dp[start[i]][endth[j]]);
		}
		if(INF == ans) puts("take a taxi");
		else printf("%d\n", ans+1);
	}
	return 0;
}
/*
5
00000000 1 1
03231130 5 5
5
a 1 1
b 1000 1000
c 3000 3000
d 3000 4000
e 4500 4000
3
3
a b c
2
c d
2
d e
00000000 1 1
03231130 5 5
5
a 1 1
b 1000 1000
e 3000 3000
d 3000 4000
c 4500 4000
2
3
a b c
3
c d e
00000000 1 1
03231130 5 5
5
a 1 1
b 1000 1000
c 3000 3000
d 3000 4000
e 4500 4000
2
3
a b c
3
c d e
00000000 1 1
00001000 3 3
4
a 1 1
b 20 30
c 40 50
d 100 100
2
3
a b c
3
b c d
00000000 1 1
03231130 5 5
4
a 1 1
b 1000 1000
c 3000 3000
d 3000 4000
2
3
a b c
3
b c d

3
1
2
walk there
take a taxi
*/


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值