POJ 3767 I Wanna Go Home(Dijkstra)

题目链接:http://poj.org/problem?id=3767

题意:(引自:http://www.cnblogs.com/mycapple/archive/2012/08/21/2649607.html

由于战争,一个商人想从城市1,回到自己的家城市2,其中城市1始终是由领导1,城市2始终由领导2,其中,商人回家的路中只能有一条路上连接由两个领导领导的城市,还有就是给出的路上双向的,也就是如果城市1能到城市2,那么城市2也同样能到城市1,这是关键。
由于这个题中从1类城市走到2类城市后就不能再回去,只能穿过一次,所以先存为双向路,后面再根据不同类型的城市,把双向路变为单向路
N个城市,城市编号为1,2,3,……N
M条路,路为双向路。 提供每条路的长度及端点城市编号,每两个城市之间直接连通的路最多一条。
这N个城市分为两个集合,部分属于集合1,部分属于集合2,提供每个城市所属的集合,现要求从城市1出发去城市2,要求途径的路中最多出现一条其两端城市不属于同一个集合 的路,求出符合该条件下的从城市1到城市2的最短路,若不存在符合条件的路,输出-1
注:数据中城市1必定属于集合1,城市2必定属于集合2 

题解:

(1)由于城市1与城市2所属的集合固定,故在路径中必定有一条而且只能有一条路从属于集合 1的城市出发进入城市2,而定不会出现从集合2走向集合1的城市。
如此,M条路中凡是连接属于不同集合的城市的路为单向路,只能从集合1中的城市走向集合2, 在此基础上用Dijkstra算法求最短路即可。

(2)其实还可以在每个阵营中做一次dijkstra,然后枚举连接两个阵营的边,做和取最小。

代码:

(1)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX=600+5;
const int INF=0x3f3f3f3f;
int g[MAX][MAX];
int dis[MAX];
int cmp[MAX];
int vis[MAX];
int n,m;
void dijkstra()
{
	for(int i=1;i<=n;i++)
	{
		dis[i]=g[1][i];
		vis[i]=0;
	}
	vis[1]=1;
	for(int i=1;i<=n;i++)
	{
		int p=-1,mmin=INF;
		for(int j=1;j<=n;j++)
		{
			if(!vis[j]&&dis[j]<mmin)
			{
				p=j;
				mmin=dis[j];
			}
		}
		if(p!=-1)
		{
			vis[p]=1;
			for(int j=1;j<=n;j++)
			{
				if(!vis[j])
				{
					dis[j]=min(dis[j],dis[p]+g[p][j]);
				}
			}
		}
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	while(scanf("%d",&n)&&n)
	{
		scanf("%d",&m);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
				g[i][j]=i==j?0:INF;
		}
		while(m--)
		{
			int a,b,t;
			scanf("%d%d%d",&a,&b,&t);
			g[a][b]=g[b][a]=t;
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&cmp[i]);
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(cmp[i]==1&&cmp[j]==2)
					g[j][i]=INF;
			}
		}
	/*	for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				printf("%d ",g[i][j]);
			}
			printf("\n");
		}*/
	/*	for(int i=1;i<=n;i++)
			printf("%d ",dis1[i]);
		printf("\n");
		for(int i=1;i<=n;i++)
			printf("%d ",dis2[i]);
		printf("\n");*/
		dijkstra();
		if(dis[2]==INF)
			printf("-1\n");
		else 
			printf("%d\n",dis[2]);
	}
	return 0;
}
(2)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX=600+5;
//const int INF=0x3f3f3f3f;
const int INF=10000000;
int g[MAX][MAX];
int dis1[MAX],dis2[MAX];
int cmp[MAX];
int n,m;
void dijkstra(int st,int *dis)
{
	int vis[MAX];
	for(int i=1;i<=n;i++)
	{
		dis[i]=g[st][i];
		vis[i]=0;
	}
	vis[st]=1;
	for(int i=1;i<=n;i++)
	{
		if(cmp[i]==st)
		{
			int p=-1,mmin=INF;
			for(int j=1;j<=n;j++)
			{
				if(cmp[j]==st&&!vis[j]&&dis[j]<mmin)
				{
					p=j;
					mmin=dis[j];
				}
			}
			if(p!=-1)
			{
				vis[p]=1;
				for(int j=1;j<=n;j++)
				{
					if(cmp[j]==st&&!vis[j])
					{
						dis[j]=min(dis[j],dis[p]+g[p][j]);
					}
				}
			}
		}
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	while(scanf("%d",&n)&&n)
	{
		scanf("%d",&m);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
				g[i][j]=i==j?0:INF;
		}
		while(m--)
		{
			int a,b,t;
			scanf("%d%d%d",&a,&b,&t);
			g[a][b]=g[b][a]=t;
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&cmp[i]);
		}
	/*	for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				printf("%d ",g[i][j]);
			}
			printf("\n");
		}*/
		dijkstra(1,dis1);
		dijkstra(2,dis2);
	/*	for(int i=1;i<=n;i++)
			printf("%d ",dis1[i]);
		printf("\n");
		for(int i=1;i<=n;i++)
			printf("%d ",dis2[i]);
		printf("\n");*/
		int mmin=INF;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(cmp[i]==1&&cmp[j]==2)
				{
					mmin=min(mmin,dis1[i]+g[i][j]+dis2[j]);
				}
			}
		}
		if(mmin>=INF)//不是==
			printf("-1\n");
		else 
			printf("%d\n",mmin);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值