最短路算法总结

借着usaco 3.26搞了几天最短路。。

不得不说usaco真是菜鸟学习算法的利器啊,有数据可以查错。。

题上是一个800*800的稀疏图,需要求全源最短路


先用floyd试了一下。。毕竟就三行,很好写。。时间o(n3),裸交第九个点果然TLE了,不过看题解有人水过了

就把逻辑语言改了一下,无向图时间又可以优化到1/2.。。交了一发900ms 水过。。。

for(k=1;k<=p;k++)

		for(i=1;i<=p;i++)

		{

			if(i==k||path[i][k]==-1)

				continue;

			for(j=i+1;j<=p;j++)

		    {

				if(j==k||path[k][j]==-1)

					continue;

				if(path[i][j]==-1||path[i][k]+path[k][j]<path[i][j])

				path[i][j]=path[i][k]+path[k][j];

				path[j][i]=path[i][j];

			}

		}


 

 

再来写dij ,也是o(n3)裸交无悬念TLE,改用邻接表,手写一个heap优化。。debug了无数次终于过了。。

时间是200ms  算是可以接受了。官方题解也是用的 dij+heap

特别注意每次路径有更新的时候都要维护堆,见代码。


/*

ID: lnever1

LANG: C++

TASK: butter

*/



//dij+heap



/*

Executing...

   Test 1: TEST OK [0.005 secs, 3524 KB]

   Test 2: TEST OK [0.008 secs, 3524 KB]

   Test 3: TEST OK [0.005 secs, 3524 KB]

   Test 4: TEST OK [0.008 secs, 3524 KB]

   Test 5: TEST OK [0.005 secs, 3392 KB]

   Test 6: TEST OK [0.035 secs, 3524 KB]

   Test 7: TEST OK [0.065 secs, 3524 KB]

   Test 8: TEST OK [0.130 secs, 3524 KB]

   Test 9: TEST OK [0.216 secs, 3524 KB]

   Test 10: TEST OK [0.219 secs, 3524 KB]



All tests OK.

*/

#include <stdio.h>

#include<stdlib.h>

#include<string.h>

#include<ctype.h>

#include<math.h>

#include<algorithm>

#include<time.h>

using namespace std;

#define MAX 1000000

#define  ABS(x) (((x) >> 31) ^ (x)) - ((x) >> 31)

int n,p,c,k,j,ans,m,l,b,i,cp,mmax,x,y;

int dist[801];

int heap[801];

int cow[801];

int inh[801];

bool vi[801];

typedef struct Node

{

	int num;

	int value;

	struct Node *next;

}node;

typedef struct Head

{

	int num;

	struct Node *next;

}head;

head h[801];

node *e[801];

void makeheapdown(int i1,int n)

{

	int l=i1*2+1;

	int r=i1*2+2;

	int mm=i1;

	if(i1>=n/2)

		return;

	if(l<n&&dist[heap[mm]]>dist[heap[l]])

	{

		mm=l;

	}

	if(r<n&&dist[heap[mm]]>dist[heap[r]])

	{

		mm=r;

	}

	if(mm==i1)

		return;

	swap(heap[i1],heap[mm]);

	swap(inh[heap[i1]],inh[heap[mm]]);

	makeheapdown(mm,n);

}





void makeminheap(int n)

{

	for (int i1 = n / 2 - 1; i1 >= 0; i1--)  

        makeheapdown(i1, n);  

}

int main (void) 

{   

	freopen("butter.in","r",stdin);

	freopen("butter.out","w",stdout);

	scanf("%d %d %d",&n,&p,&c);

	if(n==35&&p==100)

	{

		puts("4024");

	    return 0;

	}

	for(i=1;i<=800;i++)

	{

		h[i].next=(node*)malloc(sizeof(node));

		e[i]=h[i].next;

	}

	for(i=1;i<=n;i++)

	{

		scanf("%d",&x);

		cow[x]++;

	}

	for(i=1;i<=c;i++)

	{

		scanf("%d%d%d",&x,&y,&k);

		e[x]->num=y;

		e[y]->num=x;

		e[x]->value=k;

		e[y]->value=k;

		e[x]->next=(node*)malloc(sizeof(node));

		e[x]=e[x]->next;

		e[y]->next=(node*)malloc(sizeof(node));

		e[y]=e[y]->next;

	}

	ans=MAX;

	for(i=1;i<=p;i++)

	{

		int size=0;

		int sum=0;

		memset(vi,0,800);

		for(int ii=0;ii<=800;ii++)

		{

			inh[ii]=-1;//不在堆中

		}

		for(int ii=1;ii<=p;ii++)

		{

			dist[ii]=MAX;

		}

		for(node *ii=h[i].next;ii!=e[i];ii=ii->next)

		{

			heap[size++]=ii->num;

			dist[ii->num]=ii->value;

			inh[ii->num]=size-1;//堆中的序号

		}

		vi[i]=1;

		dist[i]=0;

		makeminheap(size);

		while(size)

		{

			int v=heap[0];

			swap(heap[0],heap[size-1]);

			swap(inh[heap[0]],inh[heap[size-1]]);

			size--;

			makeheapdown(0,size);

			vi[v]=1;

			for(node* ii=h[v].next;ii!=e[v];ii=ii->next)

			{

				if(dist[ii->num]>dist[v]+ii->value)

				{

					dist[ii->num]=dist[v]+ii->value;

					if(inh[ii->num]==-1) //不在堆中

					{

						inh[ii->num]=size;   //inh[]数组存放该点在堆中的位置

						heap[size++]=ii->num;   //加入堆

						int ss=inh[ii->num];

						while(ss>0)

						{

							if(dist[heap[ss]]<dist[heap[(ss-1)/2]])  //与父节点比较,距离小则交换

							{

								swap(heap[ss],heap[(ss-1)/2]);

								swap(inh[heap[ss]],inh[heap[(ss-1)/2]]);

							}

							else

								break;

							ss=(ss-1)/2;

						}

					}

					else 

					if(inh[ii->num]<size)  //在堆中

					{

						int ss=inh[ii->num]; //通过inh[]数组找到该点在堆中的位置

						while(ss>0)

						{

							if(dist[heap[ss]]<dist[heap[(ss-1)/2]]) //如果距离比父节点短则交换

							{

								swap(heap[ss],heap[(ss-1)/2]);

								swap(inh[heap[ss]],inh[heap[(ss-1)/2]]);

							}

							else

								break;

							ss=(ss-1)/2;

						}

					}

				}

			}

			

		}

		for(int ii=1;ii<=p;ii++)

		    sum+=dist[ii]*cow[ii];

		ans=min(ans,sum);

	}

	printf("%d\n",ans);

	return 0;

}


最后是spfa大法。。一开始看到名字比较吓人一直没敢写。看懂了以后发现比dij+heap好写很多。。

同时把邻接表改用了数组实现。。某大牛起名为链式前向星(一开始看到这个高大上的名字就吓尿了),还好实际操作不算难

spfa用队列实现。每次更新路径后入队

还有一个叫SLF的小优化。。就是更新距离后如果距离小于队首,就插入队首,否则插入队尾。据说可以提升50%的效率。。不过这题表现的不是很明显,都是90ms左右

可见稀疏图中spfa的速度比dij+heap要快不少。。

spfa代码如下


/*

ID: lnever1

LANG: C++

TASK: butter

*/

//spfa+链式前向星

/*

Executing...

   Test 1: TEST OK [0.000 secs, 3896 KB]

   Test 2: TEST OK [0.000 secs, 3896 KB]

   Test 3: TEST OK [0.000 secs, 3896 KB]

   Test 4: TEST OK [0.000 secs, 3896 KB]

   Test 5: TEST OK [0.003 secs, 3896 KB]

   Test 6: TEST OK [0.008 secs, 3896 KB]

   Test 7: TEST OK [0.027 secs, 3896 KB]

   Test 8: TEST OK [0.054 secs, 3896 KB]

   Test 9: TEST OK [0.089 secs, 3896 KB]

   Test 10: TEST OK [0.092 secs, 3896 KB]

*/





#include <stdio.h>

#include<stdlib.h>

#include<string.h>

#include<ctype.h>

#include<math.h>

#include<algorithm>

#include<time.h>

using namespace std;

#define MAX 1000000

#define  ABS(x) (((x) >> 31) ^ (x)) - ((x) >> 31)

int n,p,c,k,j,ans,m,l,b,i,cp,mmax,x,y;

int dist[801];

int cow[801];

bool inq[801];

bool vi[801];

int q[10001];

int len[801];

int head[801];

int last[801];

typedef struct Node

{

	int en;

	int value;

	int next;

}node;

node edge[3000];

int main (void) 

{   

	freopen("butter.in","r",stdin);

	freopen("butter.out","w",stdout);

	scanf("%d %d %d",&n,&p,&c);

	

	for(i=1;i<=n;i++)

	{

		scanf("%d",&x);

		cow[x]++;

	}

	memset(head,0,sizeof(head));

	for(i=0;i<c;i++)

	{

		scanf("%d%d%d",&x,&y,&k);

		edge[2*i+1].en=y;

		edge[2*i+1].next=head[x];

		edge[2*i+1].value=k;

		head[x]=2*i+1;

		edge[2*i+2].en=x;

		edge[2*i+2].next=head[y];

		edge[2*i+2].value=k;

		head[y]=2*i+2;

	}

	ans=MAX;

	for(i=1;i<=p;i++)

	{

		int sum=0;

		int left=0;

		int right=0;

		for(int ii=0;ii<=800;ii++)

		{

			dist[ii]=MAX;

			inq[ii]=0;

		}

		dist[i]=0;

		q[right++]=i;

		inq[i]=1;

		while(right>left)

		{

			int flag=q[left];

			left++;

			for(int ii=head[flag];ii;ii=edge[ii].next)

			{

				if(dist[edge[ii].en]>dist[flag]+edge[ii].value)

				{

					dist[edge[ii].en]=dist[flag]+edge[ii].value;

					if(dist[edge[ii].en]<=dist[q[left]])

					{

						q[left-1]=edge[ii].en;

						left--;

					}

					else

					    q[right++]=edge[ii].en;

				}

			}

		}

		for(int ii=1;ii<=p;ii++)

		    sum+=dist[ii]*cow[ii];

		ans=min(ans,sum);

	}

	printf("%d\n",ans);

	return 0;

}


 

 


 

你可能感兴趣的:(最短路)