sgu 103 Traffic Lights 最短路径

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 333
#define INF 0x7fffffff
#define min(a,b) ((a<b)?a:b)
struct node{
	int r,tb,tp;//r表示刚开始r时间后变换灯,tb表示蓝灯持续时间,tp是紫灯
	char color[2];//刚开始灯的颜色
}e[maxn];
int map[maxn][maxn],start,en,m,n,d[maxn],s[maxn],pre[maxn];
void find(int time,int x,int &px,char sx[])//px表示还要多久要变换,sx始现在的颜色
{
	if(time<e[x].r)
	{
		px=e[x].r-time;
		sx[0]=e[x].color[0];
	}
	else
	{
		int t;
		t=(time-e[x].r)%(e[x].tb+e[x].tp);
		if(e[x].color[0]=='B')
		{
			if(t<e[x].tp)
			px=e[x].tp-t,sx[0]='P';
			else
			px=e[x].tb+e[x].tp-t,sx[0]='B';
		}
		else
		{
			if(t<e[x].tb)
			px=e[x].tb-t,sx[0]='B';
			else
			px=e[x].tb+e[x].tp-t,sx[0]='P';
		}
	}
}
int findtime(int time,int x,int y,int num)
{
	if(num>3)//同时变换后还要再找时间,3次同时变换说明死循环,这条路不能通过
                /*
                循环两次的例子,现在时间是15,一点P 1 16 25,第一次变换(P,17),第二次变换(B,45),第三次变换(P,58)
                                              二点P 17 25 15,第一次变换(P,17),第二次变换(B,45),第三次变换(P,57)
                */
	return -1;
	int px,py;//x,y点,还要灯变化还需要的时间
	char sx[2],sy[2];//x,y点灯的颜色
 	find(time,x,px,sx);
 	find(time,y,py,sy);
 	if(sx[0]==sy[0])
 	return time;
 	if(px==py)
 	return findtime(time+px,x,y,num+1);//同时变换后再找变换点
 	time+=min(px,py);//较小的变换时间,就是到那个店的最小时间
	return time;
}
void dij()//用最段路径求解
{
	int i,j,k,time;
	for(i=0;i<n;i++)
	{
		int min=INF,u;
		for(j=1;j<=n;j++)
		if(s[j]==0&&d[j]<min)
		{
			min=d[j];
			u=j;
		}
		s[u]=1;
		for(j=1;j<=n;j++)
		{
			if(map[u][j]!=-1&&s[j]==0)
			{
				time=findtime(d[u],u,j,1);
				if(time==-1)
				continue;
				time+=map[u][j];
				if(time<d[j])
				{
					d[j]=time;
					pre[j]=u;
				}
			}
		}
	/*	for(j=1;j<=n;j++)
		printf("%11d",d[j]);
		printf("\n");*/
	}
}
int main()
{
	int i,j,k;
	scanf("%d%d",&start,&en);
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
		d[i]=INF;
	memset(map,-1,sizeof(map));
	memset(s,0,sizeof(s));//标记走过的节点
	memset(pre,-1,sizeof(pre));//记录路径
	for(i=1;i<=n;i++)
		scanf("%s%d%d%d",e[i].color,&e[i].r,&e[i].tb,&e[i].tp);
	for(i=0;i<m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		map[a][b]=map[b][a]=c;
	}
	d[start]=0;
	pre[start]=start;
	dij();
	if(pre[en]==-1)
	printf("0\n");
	else{
		printf("%d\n",d[en]);
		int p,q,t=0,load[maxn];
		p=en;
		while(p!=pre[p])//路径输出
		{
			load[t++]=p;
			p=pre[p];
		}
		printf("%d",start);
		for(i=t-1;i>=0;i--)
		printf(" %d",load[i]);
		printf("\n");
	}
	return 0;
}
/*
题意:
    知道始末两点,各点间所需时间,求两点间的最短路径。
    各点蓝紫两色灯,循环变换
    其中两点间可通过的条件是:两点的灯色相同
    
    方法就是在最短路径中换算下一节点是加上两点灯色变换的时间。
原理:
给的图是一个动态的图,边权非负且在发生周期性的变化。要求两点之间的最短路。

  考虑Dijstra还能不能用。Dijstra的过程是不断将距离源点最近的点加入集合S,
直到目标点也被加入S。Dijstra要求随着路径上节点数目的增加,路径长度也相应增加,即不能出现负权边。
他的贪心基于这样的考虑:若能尽早到达某个点,就尽早到达,尽早到达这个点时扩展出的经过这个点的路径,
比较晚达到这个点时扩展出的经过这个点的路径短。这个题中,边权的变化是有规律的,我发现尽早到达的想法仍然正确。
所以Dijstra可以用。

  下面想想怎么求某一时刻的边权。
边权有两个组成部分:一部分是通过这条道路需要的时间,另一部分是在路口等待的时间。
给出时刻t,可以求得道路两端的灯的颜色。
若颜色相同,则等待时间即为0;若颜色不同,则需等到某一个灯换颜色为止,特殊的,两个灯同时换了颜色,
则需要再等某个灯换颜色,如果出现两个灯连续三次同时变换颜色,则这条道路永远无法通行:

  设某时刻灯A为紫色,灯B为蓝色。第一次同时变色后,灯A为蓝色,灯B为紫色。而后两灯第二次同时变色,
说明灯A的蓝色周期与灯B的紫色周期相等。第三次同时变色,说明灯A的紫色周期与灯B的蓝色周期相等。
即,两灯颜色变换的总周期相同而相位相反,说明这条道路无法通行了。
*/



转自:http://d.ream.at/sgu-103/的说明和http://hi.baidu.com/wqqr_code/item/c142501a4583c4071894ecf3的代码,有所修改(为了让自己明白,嘿嘿)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值