UVA 1416
题目链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=447&problem=4162&mosmsg=Submission+received+with+ID+16031798
题意:
给一个图n个点,m条边,n <= 100,m <= 1000。
问两个值。第一个值是原图中每个点到其余点最短路的和。
第二个值是删除一条边之后,每个点到其余点最短路和的最大值。
思路:
看了题解打的。
第一个值的思路很简单,对每个点做一次原始的Dijkstra。
第二个值就比较麻烦了。题解采用一种标记边的方法。如果当前删除边在原最短路中,则重新做一次dijkstra。否则直接用原来值就行。
然而并不知道怎么实现。上网查别人代码发现一种最优美的姿势存一个use[s][cnt]是对一个起点s,把所有在dis[v]<dis[s]+val中遍历过的边都标记use[s][edge_num]=1。
然而WA点出现了。自己写的时候如果删除边在最短路中,则更新这个点的sum[s]值(最短路路径和)然而这样是错的。因为更新后不能保证他是最短路了。所以dijkstra时要分两种情况讨论。一种要更新use数组,一种则不要。
源码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
#define N 1005
#define LL long long
#define inf (1000000000000)
int n, m;
LL L;
int head[N], cnt;
struct Edge
{
int v, ne;
LL val;
Edge(){}
Edge(int _u, int _v, LL _val){v = _v, val = _val, ne = head[_u];}
}edge[N * 2];
void add_edge(int u, int v, LL val)
{
edge[cnt] = Edge(u, v, val);
head[u] = cnt++;
}
struct D
{
int d, u;
bool operator < (const D &rbs)const
{
return d > rbs.d;
}
};
priority_queue<D>que;
int use[N][N * 2];
LL dis[N][N];
int vis[N];
LL sum[N];
LL sum1, sum2;
int del[N*2];
LL dijkstra(int s, int op)
{
LL ans = 0;
for(int i = 1 ; i <= n ; i++)
dis[s][i] = inf, vis[i] = 0;
if(op == 0)
memset(use[s], 0, sizeof(use[s]));
dis[s][s] = 0;
while(!que.empty()) que.pop();
que.push(D{0, s});
while(!que.empty()){
int org = que.top().u; que.pop();
if(vis[org]) continue;
vis[org] = 1;
for(int now = head[org] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(del[now]) continue;
if(dis[s][v] > dis[s][org] + edge[now].val){
dis[s][v] = dis[s][org] + edge[now].val;
if(op == 0)
use[s][now] = use[s][now^1] = 1;
que.push(D{dis[s][v], v});
}
}
}
for(int i = 1 ; i <= n ; i++){
if(dis[s][i] < inf)
ans += dis[s][i];
else
ans += L;
}
return ans;
}
int main()
{
// freopen("UVA 1416.in", "r", stdin);
while(scanf("%d%d%lld", &n, &m, &L) != EOF){
int u, v;
LL val;
cnt = 0;
memset(head, -1, sizeof(head));
for(int i = 1 ; i <= m ; i++){
scanf("%d%d%lld", &u, &v, &val);
add_edge(u, v, val);
add_edge(v, u, val);
}
sum1 = sum2 = 0;
for(int i = 1 ; i <= n ; i++)
sum[i] = dijkstra(i, 0), sum1 += sum[i];
sum2 = 0;
memset(del, 0, sizeof(del));
for(int i = 0 ; i < m ; i++){
LL temp = 0;
for(int j = 1 ; j <= n ; j++){
if(use[j][i*2]){
del[i*2] = del[(i*2)^1] = 1;
temp += dijkstra(j, 1);
del[i*2] = del[(i*2)^1] = 0;
}
else
temp += sum[j];
}
sum2 = max(sum2, temp);
}
printf("%lld %lld\n", sum1, sum2);
}
return 0;
}