最短路算法总结

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

 


 

 


 

 

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