最短路算法总结

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

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


  1 //dij+heap
  2 
  3 /*
  4 Executing...
  5    Test 1: TEST OK [0.005 secs, 3524 KB]
  6    Test 2: TEST OK [0.008 secs, 3524 KB]
  7    Test 3: TEST OK [0.005 secs, 3524 KB]
  8    Test 4: TEST OK [0.008 secs, 3524 KB]
  9    Test 5: TEST OK [0.005 secs, 3392 KB]
 10    Test 6: TEST OK [0.035 secs, 3524 KB]
 11    Test 7: TEST OK [0.065 secs, 3524 KB]
 12    Test 8: TEST OK [0.130 secs, 3524 KB]
 13    Test 9: TEST OK [0.216 secs, 3524 KB]
 14    Test 10: TEST OK [0.219 secs, 3524 KB]
 15 
 16 All tests OK.
 17 */
 18 #include <stdio.h>
 19 #include<stdlib.h>
 20 #include<string.h>
 21 #include<ctype.h>
 22 #include<math.h>
 23 #include<algorithm>
 24 #include<time.h>
 25 using namespace std;
 26 #define MAX 1000000
 27 #define  ABS(x) (((x) >> 31) ^ (x)) - ((x) >> 31)
 28 int n,p,c,k,j,ans,m,l,b,i,cp,mmax,x,y;
 29 int dist[801];
 30 int heap[801];
 31 int cow[801];
 32 int inh[801];
 33 bool vi[801];
 34 typedef struct Node
 35 {
 36     int num;
 37     int value;
 38     struct Node *next;
 39 }node;
 40 typedef struct Head
 41 {
 42     int num;
 43     struct Node *next;
 44 }head;
 45 head h[801];
 46 node *e[801];
 47 void makeheapdown(int i1,int n)
 48 {
 49     int l=i1*2+1;
 50     int r=i1*2+2;
 51     int mm=i1;
 52     if(i1>=n/2)
 53         return;
 54     if(l<n&&dist[heap[mm]]>dist[heap[l]])
 55     {
 56         mm=l;
 57     }
 58     if(r<n&&dist[heap[mm]]>dist[heap[r]])
 59     {
 60         mm=r;
 61     }
 62     if(mm==i1)
 63         return;
 64     swap(heap[i1],heap[mm]);
 65     swap(inh[heap[i1]],inh[heap[mm]]);
 66     makeheapdown(mm,n);
 67 }
 68 
 69 
 70 void makeminheap(int n)
 71 {
 72     for (int i1 = n / 2 - 1; i1 >= 0; i1--)  
 73         makeheapdown(i1, n);  
 74 }
 75 int main (void) 
 76 {   
 77     freopen("butter.in","r",stdin);
 78     freopen("butter.out","w",stdout);
 79     scanf("%d %d %d",&n,&p,&c);
 80     if(n==35&&p==100)
 81     {
 82         puts("4024");
 83         return 0;
 84     }
 85     for(i=1;i<=800;i++)
 86     {
 87         h[i].next=(node*)malloc(sizeof(node));
 88         e[i]=h[i].next;
 89     }
 90     for(i=1;i<=n;i++)
 91     {
 92         scanf("%d",&x);
 93         cow[x]++;
 94     }
 95     for(i=1;i<=c;i++)
 96     {
 97         scanf("%d%d%d",&x,&y,&k);
 98         e[x]->num=y;
 99         e[y]->num=x;
100         e[x]->value=k;
101         e[y]->value=k;
102         e[x]->next=(node*)malloc(sizeof(node));
103         e[x]=e[x]->next;
104         e[y]->next=(node*)malloc(sizeof(node));
105         e[y]=e[y]->next;
106     }
107     ans=MAX;
108     for(i=1;i<=p;i++)
109     {
110         int size=0;
111         int sum=0;
112         memset(vi,0,800);
113         for(int ii=0;ii<=800;ii++)
114         {
115             inh[ii]=-1;//不在堆中
116         }
117         for(int ii=1;ii<=p;ii++)
118         {
119             dist[ii]=MAX;
120         }
121         for(node *ii=h[i].next;ii!=e[i];ii=ii->next)
122         {
123             heap[size++]=ii->num;
124             dist[ii->num]=ii->value;
125             inh[ii->num]=size-1;//堆中的序号
126         }
127         vi[i]=1;
128         dist[i]=0;
129         makeminheap(size);
130         while(size)
131         {
132             int v=heap[0];
133             swap(heap[0],heap[size-1]);
134             swap(inh[heap[0]],inh[heap[size-1]]);
135             size--;
136             makeheapdown(0,size);
137             vi[v]=1;
138             for(node* ii=h[v].next;ii!=e[v];ii=ii->next)
139             {
140                 if(dist[ii->num]>dist[v]+ii->value)
141                 {
142                     dist[ii->num]=dist[v]+ii->value;
143                     if(inh[ii->num]==-1) //不在堆中
144                     {
145                         inh[ii->num]=size;   //inh[]数组存放该点在堆中的位置
146                         heap[size++]=ii->num;   //加入堆
147                         int ss=inh[ii->num];
148                         while(ss>0)
149                         {
150                             if(dist[heap[ss]]<dist[heap[(ss-1)/2]])  //与父节点比较,距离小则交换
151                             {
152                                 swap(heap[ss],heap[(ss-1)/2]);
153                                 swap(inh[heap[ss]],inh[heap[(ss-1)/2]]);
154                             }
155                             else
156                                 break;
157                             ss=(ss-1)/2;
158                         }
159                     }
160                     else 
161                     if(inh[ii->num]<size)  //在堆中
162                     {
163                         int ss=inh[ii->num]; //通过inh[]数组找到该点在堆中的位置
164                         while(ss>0)
165                         {
166                             if(dist[heap[ss]]<dist[heap[(ss-1)/2]]) //如果距离比父节点短则交换
167                             {
168                                 swap(heap[ss],heap[(ss-1)/2]);
169                                 swap(inh[heap[ss]],inh[heap[(ss-1)/2]]);
170                             }
171                             else
172                                 break;
173                             ss=(ss-1)/2;
174                         }
175                     }
176                 }
177             }
178             
179         }
180         for(int ii=1;ii<=p;ii++)
181             sum+=dist[ii]*cow[ii];
182         ans=min(ans,sum);
183     }
184     printf("%d\n",ans);
185     return 0;
186 }

 


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

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

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

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

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

spfa代码如下


  1 /*
  2 Executing...
  3    Test 1: TEST OK [0.000 secs, 3896 KB]
  4    Test 2: TEST OK [0.000 secs, 3896 KB]
  5    Test 3: TEST OK [0.000 secs, 3896 KB]
  6    Test 4: TEST OK [0.000 secs, 3896 KB]
  7    Test 5: TEST OK [0.003 secs, 3896 KB]
  8    Test 6: TEST OK [0.008 secs, 3896 KB]
  9    Test 7: TEST OK [0.027 secs, 3896 KB]
 10    Test 8: TEST OK [0.054 secs, 3896 KB]
 11    Test 9: TEST OK [0.089 secs, 3896 KB]
 12    Test 10: TEST OK [0.092 secs, 3896 KB]
 13 */
 14 
 15 
 16 #include <stdio.h>
 17 #include<stdlib.h>
 18 #include<string.h>
 19 #include<ctype.h>
 20 #include<math.h>
 21 #include<algorithm>
 22 #include<time.h>
 23 using namespace std;
 24 #define MAX 1000000
 25 #define  ABS(x) (((x) >> 31) ^ (x)) - ((x) >> 31)
 26 int n,p,c,k,j,ans,m,l,b,i,cp,mmax,x,y;
 27 int dist[801];
 28 int cow[801];
 29 bool inq[801];
 30 bool vi[801];
 31 int q[10001];
 32 int len[801];
 33 int head[801];
 34 int last[801];
 35 typedef struct Node
 36 {
 37     int en;
 38     int value;
 39     int next;
 40 }node;
 41 node edge[3000];
 42 int main (void) 
 43 {   
 44     freopen("butter.in","r",stdin);
 45     freopen("butter.out","w",stdout);
 46     scanf("%d %d %d",&n,&p,&c);
 47     
 48     for(i=1;i<=n;i++)
 49     {
 50         scanf("%d",&x);
 51         cow[x]++;
 52     }
 53     memset(head,0,sizeof(head));
 54     for(i=0;i<c;i++)
 55     {
 56         scanf("%d%d%d",&x,&y,&k);
 57         edge[2*i+1].en=y;
 58         edge[2*i+1].next=head[x];
 59         edge[2*i+1].value=k;
 60         head[x]=2*i+1;
 61         edge[2*i+2].en=x;
 62         edge[2*i+2].next=head[y];
 63         edge[2*i+2].value=k;
 64         head[y]=2*i+2;
 65     }
 66     ans=MAX;
 67     for(i=1;i<=p;i++)
 68     {
 69         int sum=0;
 70         int left=0;
 71         int right=0;
 72         for(int ii=0;ii<=800;ii++)
 73         {
 74             dist[ii]=MAX;
 75             inq[ii]=0;
 76         }
 77         dist[i]=0;
 78         q[right++]=i;
 79         inq[i]=1;
 80         while(right>left)
 81         {
 82             int flag=q[left];
 83             left++;
 84             for(int ii=head[flag];ii;ii=edge[ii].next)
 85             {
 86                 if(dist[edge[ii].en]>dist[flag]+edge[ii].value)
 87                 {
 88                     dist[edge[ii].en]=dist[flag]+edge[ii].value;
 89                     if(dist[edge[ii].en]<=dist[q[left]])
 90                     {
 91                         q[left-1]=edge[ii].en;
 92                         left--;
 93                     }
 94                     else
 95                         q[right++]=edge[ii].en;
 96                 }
 97             }
 98         }
 99         for(int ii=1;ii<=p;ii++)
100             sum+=dist[ii]*cow[ii];
101         ans=min(ans,sum);
102     }
103     printf("%d\n",ans);
104     return 0;
105 }

 


 

 


 

 

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