题意分析:
N个点,M条边的一张图,给出每条边的距离,和建边的消耗。现在问:保持图中结点1到其它点的最短距离不变,删除掉一些边,那么构建出整张图所需要的消耗最少为多少?
解题思路:
dijkstra在寻找答案的时候我们可以用一个cost数组,记录这个结点入边的最小消耗,最终将最小消耗相加就是我们要求的答案了。
因为最终的图,除了出发点,其它点入边只有一条,所以不存在cost数组重复的情况。
那么根据定义,当距离被更新时,直接更新cost,当距离相等时,更新cost,即可。
个人感受:
第一版本把cost设为了出边的最小消耗,错误,由于出边有多条,这么记录不行。第二版换成先dijkstra再kruskal,然而无法解决保持原有的最短距离。然后和队友讨论,才知道记录入边才是正解。学习了。
具体代码如下:
#include<algorithm> #include<cctype> #include<cmath> #include<cstdio> #include<cstring> #include<iomanip> #include<iostream> #include<map> #include<queue> #include<set> #include<sstream> #include<stack> #include<string> #define ll long long #define pii pair<int, int> #define pr(x) cout << #x << " = " << (x) << '\n'; using namespace std; const int INF = 0x7f7f7f7f; const int N = 1e4 + 111; const int M = 4e4 + 111; struct E { int nxt, to, d, c; }edge[M]; int dis[N], cost[N], head[N], cnt; void add_edge(int u, int v, int d, int c) { edge[cnt].to = v; edge[cnt].c = c; edge[cnt].d = d; edge[cnt].nxt = head[u]; head[u] = cnt++; } void dijkstra(int s) { dis[s] = 0; priority_queue<pii, vector<pii>, greater<pii> > pq; pq.push(pii(dis[s], s)); while (pq.size()) { pii cur = pq.top(); pq.pop(); int u = cur.second, dist = cur.first; if (dis[u] < dist) continue; for (int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].to; if (dis[v] > dis[u] + edge[i].d) { dis[v] = dis[u] + edge[i].d; pq.push(pii(dis[v], v)); cost[v] = edge[i].c; } else if (dis[v] == dis[u] + edge[i].d) { // 路径长度相同,比较花费 if (cost[v] > edge[i].c) { cost[v] = edge[i].c; } } } } } int main() { #ifdef LOCAL freopen("C:\\Users\\apple\\Desktop\\in.txt", "r", stdin); #endif int n, m; while (~scanf("%d%d", &n, &m) && (n | m)) { cnt = 0; int u, v, d, c; for(int i = 1; i <= n; ++i) { head[i] = -1; cost[i] = dis[i] = INF; } for (int i = 0; i < m; ++i) { scanf("%d%d%d%d", &u, &v, &d, &c); add_edge(u, v, d, c); add_edge(v, u, d, c); } dijkstra(1); int sum = 0; for (int i = 1; i <= n; ++i) { if (cost[i] != INF) sum += cost[i]; } printf("%d\n", sum); } return 0; }