题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2081
题目大意:给你n个点m条双向边的图。d(i,j)表示以 i 为源点时,j 到i的最短路。让你算 c = d(1,1)+d(1,2)+..+.d(1,n)+d(2,1)+d(2,2)+...+d(2,n)+......+d(n,n),如果d(i,j)不连通, + 的时候就用输入的 l 代替,。然后你可以删掉一条边,使得上面那个 c 最大,并求出这个最大值。
解题思路:第一个问题很好解决,floyd 和 n 次 dij 或者 spfa 都可以。关键是删掉一条边那里。如果枚举删掉的边,那复杂度就是 m*n^3 ,换 dij 和 spfa 应该也差不多,都过不了。此时就要想到,不是无论删除哪条边,所有东西都要重新算的,只有那些在最短路树上的边被删除时,才需要重新算。所以就是先 dij 处理出 n 个最短路树和每个为源点时的最短路和。然后就是上面的步骤了,先判断要不要重新算就好了。这里有个陷阱的,就是有重边。如果你要删除了两个点之间的一条边,那么肯定是删的最短的那一条,因为要使 c 最大么,删掉后不是就没有边了,应该是第二短的边顶上,所以这里还要稍微处理一下。
其实我不会算上面这个算法的复杂度,求哪位好心的大神指点~~~
代码如下:
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long lld;
const int INF = 0x0fffffff;
const int MAXN = 111;
vector<int> G[MAXN][MAXN];
bool flag[MAXN][MAXN][2];
void init(int n)
{
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
G[i][j].clear();
}
void add_edge(int s,int t,int val)
{
G[s][t].push_back(val);
if(G[s][t].size() <= 2) flag[s][t][G[s][t].size()-1] = 1;
}
struct Node
{
int id;
int val;
Node(int iid,int vval)
{
id = iid;
val = vval;
}
bool operator < (const Node& tmp) const
{
return val > tmp.val;
}
};
priority_queue<Node> q;
bool done[MAXN];
int d[MAXN];
int p[MAXN][MAXN];
int first;
void dij(int s,int n)
{
for(int i = 1;i <= n;i++)
d[i] = INF;
d[s] = 0;
p[s][s] = -1;
q.push(Node(s,0));
memset(done,0,sizeof(done));
while(!q.empty())
{
Node cur = q.top();
q.pop();
if(done[cur.id]) continue;
done[cur.id] = 1;
for(int i = 1;i <= n;i++)
for(int j = 0;j < G[cur.id][i].size();j++)
{
if(flag[cur.id][i][j])
{
int tmp = cur.val+G[cur.id][i][j];
if(tmp < d[i])
{
d[i] = tmp;
if(first) p[s][i] = cur.id;
q.push(Node(i,tmp));
}
break;
}
}
}
}
lld cc[MAXN];
int main()
{
int n,m,l;
while(~scanf("%d%d%d",&n,&m,&l))
{
init(n);
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
}
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
sort(G[i][j].begin(),G[i][j].end());
lld c = 0;
first = 1;
for(int i = 1;i <= n;i++)
{
dij(i,n);
cc[i] = 0;
for(int j = 1;j <= n;j++)
{
if(d[j] >= INF) cc[i] += l;
else cc[i] += d[j];
}
c += cc[i];
}
first = 0;
printf("%lld ",c);
lld ans = 0;
for(int i = 1;i <= n;i++)
{
for(int j = i+1;j <= n;j++)
if(G[i][j].size())
{
flag[i][j][0] = flag[j][i][0] = 0;
lld tmp = 0;
for(int s = 1;s <= n;s++)
if(p[s][i] == j || p[s][j] == i)
{
dij(s,n);
for(int k = 1;k <= n;k++)
if(d[k] >= INF) tmp += l;
else tmp += d[k];
}
else tmp += cc[s];
flag[i][j][0] = flag[j][i][0] = 1;
ans = max(ans,tmp);
}
}
printf("%lld\n",ans);
}
return 0;
}