题目大意:https://www.cnblogs.com/Juve/articles/11219089.html
那一天,我们……行啦,不要帮出题人脑补画面了,我们来正经的题解
我们发现我们可以把与1号节点相连的所有节点取出,如果我们把最小环在1号节点处断开,那么最小环断成的链一定是以这些节点中的某一个节点作为起点,另一个节点作为终点的一条路路径。
如果不考虑时间复杂度,我们完全可以枚举作为起点的节点,每次都跑一遍最短路来更新答案。
但是上面的做法肯定会爆炸,所以我们考虑如何降低复杂度。
我们考虑把所有的节点分为两组,一组中的所有点作为起点,另一组中的所有点作为终点,一起跑最短路,更新答案。
如此我们发现只要我们能够保证真正贡献答案的一对节点会在某一次分组当中被分到不同组,我们就可以保证算法的正确性。
因为起点与终点的编号肯定不相同,于是我们可以按照二进制分组,枚举每个二进制位,按照当前二进制位的0/1情况来进行分组。
我看好像没有人用这种方法的,其实dij就可以过,用dij求1号节点开始的最小环
别告诉我你不会求最小环。
我们枚举1号点连接的每一条边,设起点为fr[i],终点为to[i],
先把这条边权变成0x3f3f3f3f,跑dij,求出此时fr[i]到to[i]的最短路,再加上原来这条边的权值就是一个环的大小,
更新答案:ans=min(dis[to[i]]+w[i]);
就是把fr[i]和to[i]间的边断开后两点间的最短路加上原来两点间的距离取最小值
原来博主想用tarjan求边双,然后在1号节点所在的边双中跑dij,但时间并没有下去
还有注意给边编号从2开始,这样i和i^1是一对双向边
本来一道很有思维量的题被做成了dijkstra模板(好像没人用正解打)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define MAXN 10005
#define MAXM 40005
using namespace std;
ll t,n,m,ans=0x3f3f3f3f;
ll pre[MAXN],tot_e=1,to[MAXM<<1],nxt[MAXM<<1],w[MAXM<<1];
void add(ll u,ll v,ll d){
tot_e++,to[tot_e]=v,nxt[tot_e]=pre[u],pre[u]=tot_e,w[tot_e]=d;
}
ll dis[MAXN];
priority_queue< pair<ll, ll> > q;
bool visit[MAXN];
void dijkstra(ll x){
memset(dis,0x3f,sizeof(dis));
dis[x]=0;
memset(visit,0,sizeof(visit));
q.push(make_pair(-dis[x],x));
while(!q.empty()){
ll y=q.top().second;q.pop();
if(visit[y]) continue;
visit[y]=1;
for(ll i=pre[y];i;i=nxt[i]){
ll v=to[i],z=w[i];
if(dis[v]>dis[y]+z){
dis[v]=dis[y]+z;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
for(ll i=1,u,v,d;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&d);
add(u,v,d),add(v,u,d);
}
for(ll i=pre[1];i;i=nxt[i]){
ll temp=w[i];
w[i]=w[i^1]=0x3f3f3f3f;
dijkstra(1);
ans=min(ans,dis[to[i]]+temp);
w[i]=w[i^1]=temp;
}
if(ans==0x3f3f3f3f) ans=-1;
printf("%lld\n",ans);
ans=0x3f3f3f3f;
tot_e=1;
memset(pre,0,sizeof(pre));
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define MAXN 10005
#define MAXM 40005
using namespace std;
ll t,n,m,ans=0x3f3f3f3f;
ll pre[MAXN],tot_e=,to[MAXM<<],nxt[MAXM<<],w[MAXM<<];
void add(ll u,ll v,ll d){
tot_e++,to[tot_e]=v,nxt[tot_e]=pre[u],pre[u]=tot_e,w[tot_e]=d;
}
bool vis[MAXM<<];
void dfs(ll x,ll res){
if(res>ans) return ;
if(x==){
if(res!=){
ans=min(ans,res);
return ;
}
}
for(ll i=pre[x];i;i=nxt[i]){
if(!vis[i]){
vis[i]=,vis[i^]=;
dfs(to[i],res+w[i]);
vis[i]=,vis[i^]=;
}
}
}
ll dfn[MAXN],low[MAXN],dfs_order=;
bool is_bridge[MAXM<<];
void tarjan(ll x,ll in_edge){
dfn[x]=low[x]=++dfs_order;
for(ll i=pre[x];i;i=nxt[i]){
ll y=to[i];
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>low[x])
is_bridge[i]=is_bridge[i^]=;
}
else if(i!=(in_edge^))
low[x]=min(low[x],dfn[y]);
}
}
bool belong[MAXN];
void DFS(int x){
belong[x]=;
for(int i=pre[x];i;i=nxt[i]){
int y=to[i];
if(belong[y]||is_bridge[i]) continue;
DFS(y);
}
}
ll dis[MAXN];
priority_queue< pair<ll, ll> > q;
bool visit[MAXN];
void dijkstra(ll x){
memset(dis,0x3f,sizeof(dis));
dis[x]=;
memset(visit,,sizeof(visit));
q.push(make_pair(-dis[x],x));
while(!q.empty()){
ll y=q.top().second;q.pop();
if(visit[y]) continue;
visit[y]=;
for(ll i=pre[y];i;i=nxt[i]){
ll v=to[i],z=w[i];
if(dis[v]>dis[y]+z){
dis[v]=dis[y]+z;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
if(n==m){
for(ll i=,u,v,d;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&d);
add(u,v,d),add(v,u,d);
}
dfs(,);
if(ans==0x3f3f3f3f) ans=-;
printf("%lld\n",ans);
ans=0x3f3f3f3f;
memset(pre,,sizeof(pre));
tot_e=;
}else{
for(ll i=,u,v,d;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&d);
add(u,v,d),add(v,u,d);
}
for(ll i=;i<=n;i++){
if(!dfn[i]) tarjan(i,);
}
DFS();
for(ll i=pre[];i;i=nxt[i]){
if(is_bridge[i]||!belong[to[i]]) continue;
ll temp=w[i];
w[i]=w[i^]=0x3f3f3f3f;
dijkstra();
ans=min(ans,dis[to[i]]+temp);
w[i]=w[i^]=temp;
}
if(ans==0x3f3f3f3f) ans=-;
printf("%lld\n",ans);
ans=0x3f3f3f3f;
tot_e=,dfs_order=;
memset(pre,,sizeof(pre));
memset(dfn,,sizeof(dfn));
memset(is_bridge,,sizeof(is_bridge));
}
}
return ;
}
tarjan的复杂算法
更多算法: