题目大意
给出有向图 G = { V , E } G=\{V,E\} G={V,E},从 1 号点到 2 号点再返回;到 3 号点再返回……到 N N N 号点再返回。求经过的所有边权值之和的最小值。
解题思路
考虑转化为单源最短路径问题,否则需要使用 dfs 等,容易超时。
怎么转化成单源最短路径?显然 1 号是源,那么,将边正着建,跑一遍单源最短路径;再倒着建,跑一遍单源最短路径即可。
这里选择 dijkstra。时间复杂度
O
(
N
log
N
+
M
)
O(N\log N+M)
O(NlogN+M)。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
using namespace std;
const int MAXN=1010;
const int MAXM=100010;
struct edge{
int x, y, d, next;
}e[MAXM];
int len;
int first[MAXN];
int n, m;
int u[MAXM], v[MAXM], w[MAXM];
struct point{
int d, id;
point (){
d=id=0;
}
};
bool operator< (const point a, const point b){
if (a.d!=b.d) return a.d<b.d?1:0;
return a.id<b.id?1:0;
}
set<point> s;
int h[MAXN];
void ins (int x, int y, int d){
e[++len].x=x; e[len].y=y; e[len].d=d;
e[len].next=first[x]; first[x]=len;
}
inline int read (){
int x=0; char c;
do c=getchar (); while ('0'>c||'9'<c);
while ('0'<=c&&'9'>=c)
x=x*10+c-48, c=getchar ();
return x;
}
int dijkstra (int type){
memset (first, 0, sizeof (first)); len=0;
memset (h, 63, sizeof (h)); h[1]=0;
for (int i=1; i<=m; ++i)
if (type) ins (u[i], v[i], w[i]);
else ins (v[i], u[i], w[i]);
point tmp; tmp.id=1; tmp.d=0;
s.clear (); s.insert (tmp);
for (int i=2; i<=n; ++i){
tmp=*s.begin (); s.erase (tmp);
int x=tmp.id;
for (int j=first[x]; j; j=e[j].next){
int y=e[j].y;
if (h[y]>h[x]+e[j].d){
tmp.id=y; tmp.d=h[y]; s.erase (tmp);
h[y]=h[x]+e[j].d;
tmp.d=h[y]; s.insert (tmp);
}
}
}
int cnt=0;
for (int i=2; i<=n; ++i)
cnt+=h[i];
return cnt;
}
int main (){
n=read (); m=read ();
for (int i=1; i<=m; ++i){
u[i]=read (); v[i]=read (); w[i]=read ();
}
printf ("%d", dijkstra (0)+dijkstra (1));
}