借着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 ",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 ",ans); 104 return 0; 105 }