和标算不同的做法。
先建立最短路树。
引理:最多只会经过一条非树边。
画图会发现是正确的
建立源点开始的最短路树,把源点的每一个子树染色。
枚举每条边,如果两端点颜色不同,则统计答案。
时间复杂度(O(n^3))
#include<bits/stdc++.h>
using namespace std;
#define N 410
int n,q,a[N][N],d[N],f[N],cl[N],h[N],v[N*2],nxt[N*2],dd[N],ec,w[N*2],vi[N],vv[N];
void add(int x,int y,int z){
v[++ec]=y;
w[ec]=z;
nxt[ec]=h[x];
h[x]=ec;
}
void dfs(int x,int fa,int c){
cl[x]=c;
for(int i=h[x];i;i=nxt[i])
if(v[i]!=fa){
dd[v[i]]=dd[x]+w[i];
dfs(v[i],x,c);
}
}
void dj(int s){
memset(v,0,sizeof(v));
memset(nxt,0,sizeof(nxt));
memset(h,0,sizeof(h));
memset(cl,0,sizeof(cl));
ec=0;
for(int i=0;i<=n;i++){
d[i]=1e7;
vv[i]=f[i]=dd[i]=cl[i]=0;
}
d[s]=0;
for(int i=1;i<=n;i++){
int t=0;
for(int j=1;j<=n;j++)
if(d[t]>d[j]&&!vv[j]&&vi[j])
t=j;
vv[t]=1;
if(!t)
break;
for(int j=1;j<=n;j++)
if(j!=t&&d[j]>d[t]+a[t][j]&&!vv[j]&&vi[j]){
f[j]=t;
d[j]=d[t]+a[t][j];
}
}
for(int i=1;i<=n;i++)
if(i!=s&&vi[i]&&vi[f[i]])
add(f[i],i,a[f[i]][i]);
int c=0;
for(int i=1;i<=n;i++)
if(i!=s&&vi[i]&&!cl[i]){
dd[i]=a[s][i];
dfs(i,s,++c);
}
}
int main(){
freopen("patrol.in","r",stdin);
freopen("patrol.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
if(a[i][j]==-1)
a[i][j]=1e7;
}
vi[i]=1;
}
scanf("%d",&q);
while(q--){
int op,x;
scanf("%d%d",&op,&x);
if(op)
vi[x]^=1;
else{
dj(x);
int ans=1e8;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(vi[i]&&vi[j]&&cl[i]!=cl[j]&&f[j]!=i&&f[i]!=j)
ans=min(ans,a[i][j]+dd[i]+dd[j]);
if(ans>=1e7)
puts("-1");
else
printf("%d
",ans);
}
}
}