题目大意:
给你邻接矩阵
对于每个点问你包括这个点的最小简单环的长度是多少
思路:
最短路径树+并查集
对于每个点去找它的最小路径树
因为最短的环要么是一条最短路+一条终点到起点的边(不在最短路里)
要么是两条最短路+这两条路终点之间的边
如图以1为起点右边1,2,5就是第一种情况
1,3,4就是第二种情况
之后的思路就生成最短路再枚举边
但问题又来了
怎么知道这个边是不在最短路里的?
这个时候就利用并查集,跑最短路的时候做个记录,记住它们是属于哪一条子树的
在答案中如果是不同子树的就按第二种情况去计算
(看别人题解好像也有用dfs的
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[310][310];
ll c[310];
int n;
int fa[310];
int findfa(int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
struct node
{
int u;
ll d;
bool operator<(const node& rhs)const
{
return d>rhs.d;
}
};
ll d[310];
int v[310]= {0};
void dij(int x)
{
priority_queue<node>q;
memset(v,0,sizeof(v));
memset(d,0x3f3f,sizeof(d));
d[x]=0;
q.push({x,0});
while(!q.empty())
{
node t=q.top();
q.pop();
if(v[t.u])continue;
v[t.u]=1;
for(int i=1; i<=n; i++)
{
if(a[t.u][i]>0&&a[t.u][i]<0x3f3f3f3f){
if(d[i]>d[t.u]+a[t.u][i])
{
if(t.u!=x)fa[i]=t.u;//跑过的路做个记录
d[i]=d[t.u]+a[t.u][i];
q.push({i,d[i]});
}
}
}
}
return;
}
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++){
cin>>a[i][j];
if(a[i][j]==-1)a[i][j]=0x3f3f3f3f;
}
for(int i=1; i<=n; i++)
{
ll ans=0x3f3f3f3f;
for(int j=1;j<=n;j++)fa[j]=j;
dij(i);
for(int j=1;j<=n;j++)if(fa[j]!=j)ans=min(ans,d[j]+a[i][j]);//第一种情况,因为题目没有两条双向边,子树起点只能是连着1的点,如果让fa[j]==j也行的话相当于来回走了两次
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
if(j!=i&&k!=i&&findfa(k)!=findfa(j)){
ans=min(ans,d[k]+d[j]+a[j][k]);//第二种情况
}
}
}
if(ans<0x3f3f3f3f)
cout<<ans<<endl;
else cout<<"-1"<<endl;
}
return 0;
}