poj 1511 Invitation Cards
最短路
题意: 强调是有向图 , n个点(1到n标号)m条边,求出点1到所有点的最短路之和 + 所有点到点1的最短路之和
什么?求一次最短路,然后 x 2 就是答案? 这样是错的,如果是无向图的话可以这样,因为可以逆回去走。但是有向图显然不是,点1到点a的最短路,和点a到点1的最短路是完全不同的,值不同走过的路径也不同.要求点1到所有点的最短路,直接运行一次最短路即可。但是要求所有点到点1的最短路,难道要对所有点运行一次最短路吗?一看点数就可以否定这个想法。可以这样想,如果点a到点1存在最短路,那么把这条路径的边全部取反,就是点1到点a的最短路了。所有在求了第1次最短路后,将 整个图的边取反,再求一次点1到所有点的最短路就行了,可以知道边都取反后,第一次走过的路径都不会再走到(都取反了),而从点a可能到点1的可能的边都成了点1到点a的可能的边
代码用了spfa算法,用queue和stack来辅助对时间的影响不大,但是stack稍快一点(只从上次后我发现其实stack都比queue快,一般情况下,更不用题特殊情况了)
另外用dij+heap也行,朴素的dij应该是超时的,虽然没写
#include <cstdio> #include <cstring> #include <queue> #include <stack> using namespace std; #define N 1000010 #define INF 0x3f3f3f3f typedef long long ll; ll d[N]; int n,tot; int head[N]; bool ins[N]; struct edge { int u,v,w,next; }e[N],tt[N]; void add(int u ,int v ,int w , int k) { e[k].u = u; e[k].v = v; e[k].w = w; e[k].next = head[u]; head[u] = k; } ll spfa_stack() { stack<int>sta; memset(d,0x3f,sizeof(d)); memset(ins,false,sizeof(ins)); while(!sta.empty()) sta.pop(); d[1] = 0; ins[1] = true; sta.push(1); while(!sta.empty()) { int u = sta.top(); sta.pop(); ins[u] = false; for(int k=head[u]; k!=-1; k=e[k].next) { int v =e[k].v; int w = e[k].w; if( d[u] + w < d[v] ) { d[v] = d[u] + w; if(!ins[v]) { ins[v] = true; sta.push(v); } } } } ll res = 0; for(int i=1; i<=n ; i++) res += d[i]; return res; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&tot); memset(head,-1,sizeof(head)); for(int i=0; i<tot; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); tt[i].u = u; tt[i].v = v; tt[i].w = w; add(u,v,w,i); } ll suma = spfa_stack(); memset(head,-1,sizeof(head)); for(int i=0; i<tot; i++) add(tt[i].v , tt[i].u , tt[i].w , i); ll sumb = spfa_stack(); printf("%lld\n",suma+sumb); } return 0; }