现在的位置: 首页 > 综合 > 正文

POJ1511 Invitation Cards [最短路,dijstra+heap,spfa]

2013年10月26日 ⁄ 综合 ⁄ 共 3063字 ⁄ 字号 评论关闭

题意:

给定节点数n,和边数m,边是单向边.

问从1节点出发到2,3,...n 这些节点路程和从从这些节点回来到节点1的路程和最小值。

思路:

很显然的最短路,先以1为起点进行一次最短路,然后再将边反向一下再以1为起点进行一下最短路。

这题的意义在于数据,一般的dijstra的O(N^2)显然没法过。

先用dijstra+heap试试。(以前被这个heap唬到了,其实heap直接用priority_queue实现即可)

//36412K	1829MS
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
#define llong long long
#define Abs(a) ((a)>0?(a):-(a))
#define Mod(a,b) (((a)-1+(b))%(b)+1)
using namespace std;
const int N=1000005;
const int inf=1e10;
int n,m;
int a[N][3];
struct
{
	int w,v,next;
}edge[N];
int edgehead[N];
llong d[N];
bool vis[N];
int k;
void addedge(int u,int v,int w)
{
	edge[k].v=v;
	edge[k].w=w;
	edge[k].next=edgehead[u];
	edgehead[u]=k++;
}
struct cmp//注意priority_queue的cmp写的和sort里面的cmp的区别,前者是struct,后者是函数,而且比较大小的关系刚好是相反的。
{
	bool operator()(const int a,const int b)
	{
		return d[a]>d[b];
	}
};
llong dijstra()
{
	memset(vis,0,sizeof(vis));
	priority_queue<int,vector<int>,cmp> que;
	for(int i=2;i<=n;i++)
		d[i]=inf;
	d[1]=0;
	que.push(1);
	while(!que.empty())
	{
		int u=que.top();
		vis[u]=true;
		que.pop();
		for(int i=edgehead[u];i;i=edge[i].next)//遍历节点u的每条出去的边
		{
			int v=edge[i].v;
			int w=edge[i].w;
			if(!vis[v]&&d[v]>d[u]+w)//如果是可松弛点即加入优先队列,d值越小越优先。
			{
				d[v]=d[u]+w;
				que.push(v);
			}
		}
	}
	llong ans=0;
	for(int i=1;i<=n;i++)
		ans+=d[i];
	return ans;
}
int main()
{
	int cases;
	scanf("%d",&cases);
	while(cases--)
	{
		k=1;
		memset(edgehead,0,sizeof(edgehead));
		memset(edge,0,sizeof(edge));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%d",a[i],a[i]+1,a[i]+2);
			addedge(a[i][0],a[i][1],a[i][2]);
		}
		llong ans=dijstra();
		k=1;//将图的边反向之后再来一遍
		memset(edgehead,0,sizeof(edgehead));
		memset(edge,0,sizeof(edge));
		for(int i=1;i<=m;i++)
		{
			addedge(a[i][1],a[i][0],a[i][2]);
		}
		ans+=dijstra();
		printf("%lld\n",ans);
	}
	return 0;
}

其实效果不错。

下面是spfa的实现。

//36412K	1969MS
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
#define llong long long
#define Abs(a) ((a)>0?(a):-(a))
#define Mod(a,b) (((a)-1+(b))%(b)+1)
using namespace std;
const int N=1000005;
const int inf=1e10;
int n,m;
int a[N][3];
struct
{
	int w,v,next;
}edge[N];
int edgehead[N];
llong d[N];
bool vis[N];
int k;
void addedge(int u,int v,int w)
{
	edge[k].v=v;
	edge[k].w=w;
	edge[k].next=edgehead[u];
	edgehead[u]=k++;
}
llong spfa()
{
	memset(vis,0,sizeof(vis));
	for(int i=2;i<=n;i++)
		d[i]=inf;
	d[1]=0;
	queue<int> que;
	que.push(1);
	while(!que.empty())
	{
		int u=que.front();
		que.pop();
		vis[u]=false;//注意spfa的vis和dijstra的不同点,前者是判断是否在queue里而已,后者是判断是否已经是最优的点。
		for(int i=edgehead[u];i;i=edge[i].next)
		{
			int v=edge[i].v;
			int w=edge[i].w;
			if(d[v]>d[u]+w)//这里不能写成if(!vis[v]&&d[v]>d[u]+w),原因看上一行的注释
			{
				d[v]=d[u]+w;
				if(!vis[v])
				{
					que.push(v);
					vis[v]=true;
				}
			}
		}
	}
	llong ans=0;
	for(int i=1;i<=n;i++)
		ans+=d[i];
	return ans;
}
int main()
{
	int cases;
	scanf("%d",&cases);
	while(cases--)
	{
		k=1;
		memset(edgehead,0,sizeof(edgehead));
		memset(edge,0,sizeof(edge));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%d",a[i],a[i]+1,a[i]+2);
			addedge(a[i][0],a[i][1],a[i][2]);
		}
		llong ans=spfa();
		k=1;
		memset(edgehead,0,sizeof(edgehead));
		memset(edge,0,sizeof(edge));
		for(int i=1;i<=m;i++)
		{
			addedge(a[i][1],a[i][0],a[i][2]);
		}
		ans+=spfa();
		printf("%lld\n",ans);
	}
	return 0;
}

第一道spfa题,都说spfa速度比较快又好写,但是从这题看来,dijstra+heap其实也没难写多少。

总结一下我对spfa的理解:

1.本质是维护一个队列。

只要这个队列里面还有元素,即这个最短路还可松弛,即此时此图还存在“更短路”。

2.还要注意bool vis[]数组,它只用来判断某节点是否存在队列中,因为如果已经在队列中了则无需入队,入队只会造成不必要的时间损失。

抱歉!评论已关闭.