标签:图论
原题: http://poj.org/problem?id=1511
题目大意:
单向图,需要从点1到每个点去一次,去了马上回来,再去下一个点,求往返路径和。
如果只有100个点,跑一遍floyd就可以了,这里有10w个点,不行。
朴素的dijkstra是N^2的复杂度,这里要超时。
所以这里我们用spfa这种接近2N的算法。
由于二维数组空间不够,所以只能用vector或者邻接表,因为vector的适合经常修改,本身是链表结构,每次插入数据都会消耗时间来申请内存并和前面建立联系,虽然可以支持下标访问,但是效率肯定赶不上数组的下标访问,所以有时候用vector会超时,这里我们用邻接表来存。
由于我们要求原点到每个点的来回距离,求一遍spfa我们只能得到单向距离和,而返回的路怎么求呢?
先说我们再纸上画的情况,如下图例:
我们以A为起点,跑一遍spfa能求出A到每个点的距离。
现在B到A的路只有一条,是B->C->D->A,如果我们把顺序反过来看,可以发现A->D->C->B是他的回路。如果我们把整个地图的箭头都反过来,再求一次spfa,那么就能得到最后的结果了。
参考代码如下:
#include"stdio.h"
#include"iostream"
#include"string.h"
#include"queue"
using namespace std;
typedef long long int lint;
const lint INF = 0x3F3F3F3F3F3F3F3F;
const int N= 1000005;
struct node
{
int en;
int len;
int next;
};
int n,m;
bool vis[N];
lint dis[N];
int head1[N];
int head2[N];
node maze1[N];
node maze2[N];
void spfa1()
{
memset(vis,false,sizeof(vis));
for(int i=0; i<=n; i++) dis[i]=INF;
queue <int> q;
dis[1]=0;
vis[1]=true;
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=false;
for(int i=head1[x];i!=-1;i=maze1[i].next)
{
int en=maze1[i].en;
if(dis[en]>dis[x]+maze1[i].len)
{
dis[en]=dis[x]+maze1[i].len;
if(vis[en]==false)
{
vis[en]=true;
q.push(en);
}
}
}
}
}
void spfa2()
{
memset(vis,false,sizeof(vis));
for(int i=0; i<=n; i++)
dis[i]=INF;
queue <int> q;
dis[1]=0;
vis[1]=true;
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=false;
for(int i=head2[x];i!=-1;i=maze2[i].next)
{
int en=maze2[i].en;
if(dis[en]>dis[x]+maze2[i].len)
{
dis[en]=dis[x]+maze2[i].len;
if(vis[en]==false)
{
vis[en]=true;
q.push(en);
}
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
scanf("%d %d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
maze1[i].len=z;
maze1[i].en=y;
maze1[i].next=head1[x];
head1[x]=i;
maze2[i].len=z;
maze2[i].en=x;
maze2[i].next=head2[y];
head2[y]=i;
}
lint ans=0;
spfa1();
for(int i=1; i<=n; i++)
ans=ans+dis[i];
spfa2();
for(int i=1; i<=n; i++)
ans=ans+dis[i];
printf("%I64d\n",ans);
}
return 0;
}
下面是vector的写法,虽然对于本题的数据会超时,大家也可以参考一下:
#include"stdio.h"
#include"iostream"
#include"string.h"
#include"queue"
using namespace std;
typedef long long int lint;
const lint INF = 0x3F3F3F3F3F3F3F3F;
const int N= 1000005;
struct node
{
int en;
int len;
int next;
};
int n,m;
bool vis[N];
lint dis[N];
int head1[N];
int head2[N];
node maze1[N];
node maze2[N];
void spfa1()
{
memset(vis,false,sizeof(vis));
for(int i=0; i<=n; i++) dis[i]=INF;
queue <int> q;
dis[1]=0;
vis[1]=true;
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=false;
for(int i=head1[x];i!=-1;i=maze1[i].next)
{
int en=maze1[i].en;
if(dis[en]>dis[x]+maze1[i].len)
{
dis[en]=dis[x]+maze1[i].len;
if(vis[en]==false)
{
vis[en]=true;
q.push(en);
}
}
}
}
}
void spfa2()
{
memset(vis,false,sizeof(vis));
for(int i=0; i<=n; i++)
dis[i]=INF;
queue <int> q;
dis[1]=0;
vis[1]=true;
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=false;
for(int i=head2[x];i!=-1;i=maze2[i].next)
{
int en=maze2[i].en;
if(dis[en]>dis[x]+maze2[i].len)
{
dis[en]=dis[x]+maze2[i].len;
if(vis[en]==false)
{
vis[en]=true;
q.push(en);
}
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
scanf("%d %d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
maze1[i].len=z;
maze1[i].en=y;
maze1[i].next=head1[x];
head1[x]=i;
maze2[i].len=z;
maze2[i].en=x;
maze2[i].next=head2[y];
head2[y]=i;
}
lint ans=0;
spfa1();
for(int i=1; i<=n; i++)
ans=ans+dis[i];
spfa2();
for(int i=1; i<=n; i++)
ans=ans+dis[i];
printf("%I64d\n",ans);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ 1511 Invitation Cards 邻接表 spfa算法
标签:图论
原文地址:http://blog.csdn.net/qq_27508477/article/details/47399217