最短路算法总结

借着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;
}


 

 


 

posted @ 2014-08-05 10:28  Lnever  阅读(249)  评论(0编辑  收藏  举报