• 最小环


    最小环(Tarjan (star ))

    • 旅游区可以表示为一张由 (n) 个节点 (m) 条边组成无向图。我故地重游,却发现自己只想尽快地结束这次旅游。
    • 我从景区的出发点(即 (1) 号节点)出发,却只想找出最短的一条回路重新回到出发点,并且中途不重复经过任意一条边。即:我想找出从出发点到出发点的小环。

    Input

    • 每个测试点有多组测试数据。
    • 第一行有一个正整数(T, (T≤ 10))表示数据组数。
    • 接下来对于每组数据,第一行有两个正整数 (n,m, (nle 10^4,m ≤ 4 imes 10^4)) 分别代表图的点数和边数。
    • 接下来有(m)行,每行三个整数(u,v,d)表示(u,v)之间存在一条长度为 (d, (d ≤ 10^3))的路径。
    • 保证不存在重边,自环。

    Output

    • 对于每组测试数据,输出题目中所求的最小环的长度。
    • 无解输出 (-1)

    Sample Input

    2
    3 3
    1 2 1
    2 3 1
    3 1 1
    4 5
    1 2 2
    2 3 2
    3 4 2
    1 4 2
    1 3 5
    

    Sample Output

    3
    8
    

    Hint

    • 来源:

    分析

    • 我们发现我们可以把与 (1) 号节点相连的所有节点取出,如果我们把最小环在 (1) 号节点处断开,那么最小环断成的链一定是以这些节点中的某一个节点作为起点,另一个节点作为终点的一条路路径。

    • 正解:

    这题如果数据做的厉害一点肯定也是一个巨坑的题,正解大概和上边的暴力思路相似,只是优化了一点,将与1相连的点分组(分组方法一会再说),一组作为起点,建立超级源点向这一组每个点连边权为0的边,建立超级汇点,从另外一组每个点向超级汇点连边权为0的边,跑最短路即可。如果我们可以保证最终答案的起点与终点分到了两组,求出的就是正确答案。

    接下来说nb的分组方法:

    将每个节点按照编号转化为二进制数,从第0位开始,编号为0的分一组,编号为1的分另一组,这样也就跑了十边最短路的样子。

    但这样能确保最后答案的起点和终点分到两个组吗?显然可以,因为起点和终点的编号不同,二进制为至少有一位不同,也就至少有一次被分到不同组。

    然而正解的代码好像并没有人打……

    Code

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e4+5,maxm=4e4+5;
    const int Inf=0x3f3f3f3f;
    struct Node{int to,next,w;}e[maxm<<1];
    int n,m,ans;
    int head[maxn],len,dis[maxn];
    bool vis[maxn];
    priority_queue<pair<int, int> > q;
    void Insert(int u,int v,int w){    
        e[len].to=v;e[len].w=w;e[len].next=head[u];head[u]=len++;
    }
    void Spfa(int x){
        memset(dis,0x3f,sizeof(dis));    
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(x);dis[x]=0;
        while(!q.empty()){
            int u=q.front();q.pop();
            vis[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].to;
                if(dis[v]>dis[u]+e[i].w){
                    dis[v]=dis[u]+e[i].w;
                    if(!vis[v]){
                        q.push(v);vis[v]=1;
                    }
                }
            }
        } 
    }
    void Solve(){
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            memset(head,-1,sizeof(head));
            len=0;
            for(int i=1,u,v,w;i<=m;i++){
                scanf("%d%d%d",&u,&v,&w);
                Insert(u,v,w),Insert(v,u,w);
            }
            ans=Inf;
            for(int i=head[1];~i;i=e[i].next){
                int temp=e[i].w;
                e[i].w=e[i^1].w=Inf;//断掉一条邻接边
                Spfa(1);
                ans=std::min(ans,dis[e[i].to]+temp);
                e[i].w=e[i^1].w=temp;//续上
            }
            if(ans==Inf) ans=-1;
            printf("%d
    ",ans);       
        }
    }
    int main(){
        Solve();
        return 0;
    }
    
  • 相关阅读:
    心灵絮语(一)
    JavaScript基础之对象
    asp.net多线程(收藏)
    详解:数据库名、实例名、ORACLE_SID、数据库域名、全局数据库名、服务名
    凡事求个合理
    坐班一族快速消除疲劳七妙招
    电脑前保健攻略
    《孔子》都曰了些啥?
    幸福的来源不是财富的增长,而是欲望的降低
    [转]从一个男人关注的事情上 可以看出他的修养和抱负
  • 原文地址:https://www.cnblogs.com/hbhszxyb/p/13253985.html
Copyright © 2020-2023  润新知