先生成最小生成树,再预处理出两点之间的最大边权,枚举非树边,替换两点之中的最大边,得到最小的 s u m + w − d i s t [ a ] [ b ] sum+w-dist[a][b] sum+w−dist[a][b]。
非严格的次小生成树,可以只记录最大边,要生成严格的次小生成树,需要记录最大边和次大边,防止两点之间最大边权等于非树边导致无法替换的情况。
//严格次小生成树
#include
using namespace std;
const int N = 5 * 1e2 + 5,M = 1e4 + 5;
typedef long long LL;
int n,m;
struct Edge{
int a,b,w;
bool f;
int next;
bool operator< (const Edge &t) const{
return w < t.w;
}
}edge[M],e[M];
int head[N],dist1[N][N],dist2[N][N];
int p[N];
int idx;
int find(int x){
if(x == p[x]) return x;
else return p[x] = find(p[x]);
}
void ad(int fr,int to,int dis){
idx ++;
e[idx].a = fr;
e[idx].b = to;
e[idx].w = dis;
e[idx].next = head[fr];
head[fr] = idx;
}
void dfs(int u,int fa,int dmax1,int dmax2,int d1[],int d2[]){
d1[u] = dmax1;
d2[u] = dmax2;
for(int i = head[u];i;i = e[i].next){
if(e[i].b != fa){
int td1 = dmax1,td2 = dmax2;
if(e[i].w > td1) td2 = td1,td1 = e[i].w;
else if(e[i].w > td2 && e[i].w < td1) td2 = e[i].w;
dfs(e[i].b,u,td1,td2,d1,d2);
}
}
}
int main(){
cin >> n >> m;
for(int i = 1;i <= m;i ++){
int fr,to,dis;
scanf("%d%d%d",&fr,&to,&dis);
edge[i] = {fr,to,dis};
}
LL sum = 0;
sort(edge + 1,edge + m + 1);
for(int i = 1;i <= n;i ++) p[i] = i;
for(int i = 1;i <= m;i ++){
int fr = edge[i].a;
int to = edge[i].b;
if(find(fr) != find(to)){
int a = find(fr);
int b = find(to);
p[a] = b;
sum += edge[i].w;
edge[i].f = true;
ad(edge[i].a,edge[i].b,edge[i].w);
ad(edge[i].b,edge[i].a,edge[i].w);
}
}
for(int i = 1;i <= n;i ++) dfs(i,-1,-1e9,-1e9,dist1[i],dist2[i]);
LL res = 1e18;
//cout << sum;
for(int i = 1;i <= m;i ++){
if(! edge[i].f){
int fr = edge[i].a;
int to = edge[i].b;
// cout << dist1[fr][to] << " " << dist2[fr][to] << endl;
int dis = edge[i].w;
if(dis > dist1[fr][to]) res = min(res,sum - dist1[fr][to] + dis);
else if(dis > dist2[fr][to]) res = min(res,sum - dist2[fr][to] + dis);
}
}
cout << res;
return 0;
}
基础判断负环,看讲解觉得特别基础的东西,但是写起来也遇到一些问题。
1.注意数组清空。
2.不用判断距离,假设存在负环,那么只需要从负边开始走就能存在负环,所以刚开始将所有点入队,不用每个点分别开SPFA。
3.在一个图都是正边的情况下,一个点到原点的距离不可能超过n,如果超过n,就说明存在负环,可以用边思考。
#include
using namespace std;
const int N = 510,M = 5500;
int n,m,w;
int idx;
struct Edge{
int fr,to,dis,next;
}e[M];
int head[N],cnt[N],dist[N];
bool st[N];
void ad(int fr,int to,int dis){
idx ++;
e[idx].fr = fr;
e[idx].to = to;
e[idx].dis = dis;
e[idx].next = head[fr];
head[fr] = idx;
}
bool SPFA(){
memset(cnt,0,sizeof cnt);
memset(dist,0,sizeof dist);
memset(st,true,sizeof st);
queue <int> q;
for(int i = 1;i <= n;i ++)
q.push(i);
while(! q.empty()){
int fir = q.front();
q.pop();
st[fir] = false;
for(int i = head[fir];i;i = e[i].next){
if(dist[fir] + e[i].dis < dist[e[i].to]){
cnt[e[i].to] = cnt[fir] + 1;
if(cnt[e[i].to] >= n) return true;
dist[e[i].to] = dist[fir] + e[i].dis;
if(! st[e[i].to]){
q.push(e[i].to);
st[e[i].to] = true;
}
}
}
}
return false;
}
int main(){
int t;
cin >> t;
while(t --){
cin >> n >> m >> w;
idx = 0;
memset(head,0,sizeof head);
for(int i = 1;i <= m;i ++){
int fr,to,dis;
cin >> fr >> to >> dis;
ad(fr,to,dis);
ad(to,fr,dis);
}
for(int i = 1;i <= w;i ++){
int fr,to,dis;
cin >> fr >> to >> dis;
ad(fr,to,-dis);
}
if(SPFA()) puts("YES");
else puts("NO");
}
return 0;
}
正环用最长路,负环用最短路。