• Codeforces B. Mouse Hunt(强连通分解缩点)


    题目描述:

    Mouse Hunt

    time limit per test

    2 seconds

    memory limit per test

    256 megabytes

    input

    standard input

    output

    standard output

    Medicine faculty of Berland State University has just finished their admission campaign. As usual, about 80%of applicants are girls and majority of them are going to live in the university dormitory for the next

    4(hopefully) years.

    The dormitory consists of n rooms and a single mouse! Girls decided to set mouse traps in some rooms to get rid of the horrible monster. Setting a trap in room number costs (c_i) burles. Rooms are numbered from 1 to n

    Mouse doesn't sit in place all the time, it constantly runs. If it is in room i in second then it will run to room (a_i) without visiting any other rooms inbetween (i=(a_i)means that mouse won't leave room i) It 's second 0 in the start.If the mouse is in some room with a mouse trap in it, then the mouse get caught into this trap.

    That would have been so easy if the girls actually knew where the mouse at. Unfortunately, that's not the case, mouse can be in any room from 1 to at second 0

    What it the minimal total amount of burles girls can spend to set the traps in order to guarantee that the mouse will eventually be caught no matter the room it started from?

    Input

    The first line contains as single integers n((1leq nleq 2*10^5)) — the number of rooms in the dormitory.

    The second line contains nintegers (c_1,c_2,...,c_n) (1≤(c_i)(10^4))is the cost of setting the trap in room number i

    The third line contains nintegers (a_1,a_2,...,a_n)(1≤(a_i)n) is the room the mouse will run to the next second after being in room i

    Output

    Print a single integer — the minimal total amount of burles girls can spend to set the traps in order to guarantee that the mouse will eventually be caught no matter the room it started from.

    Examples

    Input

    Copy

    51 2 3 2 101 3 4 3 3
    

    Output

    Copy

    3
    

    Input

    Copy

    41 10 2 102 4 2 2
    

    Output

    Copy

    10
    

    Input

    Copy

    71 1 1 1 1 1 12 2 2 3 6 7 6
    

    Output

    Copy

    2
    

    Note

    In the first example it is enough to set mouse trap in rooms 1 and 4. If mouse starts in room 1.then it gets caught immideately. If mouse starts in any other room then it eventually comes to room 4.

    In the second example it is enough to set mouse trap in room 2. If mouse starts in room then it gets caught immideately. If mouse starts in any other room then it runs to room 2 in second 1.

    Here are the paths of the mouse from different starts from the third example:

    1→2→2→…;

    2→2→…;

    3→2→2→…;

    4→3→2→2→…;

    5→6→7→6→…;

    6→7→6→…;

    7→6→7→…;

    So it's enough to set traps in rooms 2 and 6.

    思路:

    题目是说各一个数组a,其中i指向ai,也就是给了一个图,这个图是老鼠行程,根据这个图我们可以看到老鼠最终会在某个点停下,要么会一直的兜圈子。要求不管老鼠一开始在哪个房间,以某种代价最小的方式放置捕鼠夹,使得最后定能捕住老鼠。求这个最小代价。

    我们发现只要在老鼠停下来的地方或者环上代价最小的一个地方放置捕鼠夹就能捕住老鼠。其实这道题就可以看成是图的强连通分解,通过tarjan算法求这个图有多少个强连通块,最后求出的强连通块再进行缩点,把一个强连通块看成一个点,并存储这个强连通块上的最小代价。最后要求的点是出度为0的点,为什么?因为出度为零的点就是最后老鼠停下来的点和环(已经缩点)。

    需要注意的是缩点的一些细节,记录好强连通块的数目,每个强连通块建立对应的最小代价数组,遍历一遍点求出每个强连通块对应的最小代价,和每个强连通块的出度(只要有边相连的两个点不在一个强连通块上出度就减小一。最后遍历每个强连通块,求出出度为零的强连通块的最小代价和。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <stack>
    #define max_n 200005
    #define INF 0x3f3f3f3f
    using namespace std;
    //题目数据
    int a[max_n];
    int c[max_n];
    int n;
    //链式前向星
    int cnt = 0;
    int head[max_n];
    struct
    {
        int v;
        int next;
    }e[max_n<<1];
    void add(int u,int v)
    {
        ++cnt;
        e[cnt].v = v;
        e[cnt].next = head[u];
        head[u] = cnt;
    }
    //读入优化
    template<typename T>
    inline void read(T& x)
    {
        x = 0;int f = 0;char ch = getchar();
        while(ch<'0'||ch>'9') {f |= (ch=='-');ch = getchar();}
        while(ch>='0'&&ch<='9') {x = x*10+ch-'0';ch = getchar();}
        x = f?-x:x;
    }
    //tarjan算法
    int Bcnt = 0;
    int idx = 0;
    int dfn[max_n];
    int low[max_n];
    int instack[max_n];
    int Belong[max_n];
    stack<int> s;
    int Deg[max_n];
    int cost[max_n];
    void tarjan(int u)
    {
        dfn[u] = low[u] = ++idx;
        s.push(u);
        instack[u] = 1;
        int v;
        for(int i = head[u];i;i=e[i].next)
        {
            v = e[i].v;
            if(!dfn[v])
            {
                tarjan(v);
                low[u] = min(low[u],low[v]);
            }
            else if(instack[v])
            {
                low[u] = min(low[u],dfn[v]);
            }
        }
        if(low[u]==dfn[u])
        {
            Bcnt++;
            do
            {
                v = s.top();
                s.pop();
                instack[v] = 0;
                Belong[v] = Bcnt;
            }while(u!=v);
        }
    }
    void solve()
    {
        for(int i = 1;i<=n;i++)
        {
            cout << "d " << dfn[i] << " l " << low[i] << endl;
        }
        cout << "共有 " << Bcnt << " 个连通分量" << endl;
        for(int i = 1;i<=Bcnt;i++)
        {
            cout << "第 " << i << "个 " << endl;
            for(int j = 1;j<=n;j++)
            {
                if(Belong[j]==i)
                {
                    cout << j << " ";
                }
            }
            cout << endl;
        }
    }
    int main()
    {
        read(n);
        for(int i = 1;i<=n;i++)
        {
            read(c[i]);
        }
        for(int i = 1;i<=n;i++)
        {
            read(a[i]);
            add(i,a[i]);//建边
        }
        for(int i = 1;i<=n;i++)
        {
            if(!dfn[i])
            {
                tarjan(i);
            }
        }
        //solve();
        //缩点
        for(int i = 1;i<=Bcnt;i++)
        {
            cost[i] = INF;
        }
        for(int i = 1;i<=n;i++)
        {
            cost[Belong[i]] = min(cost[Belong[i]],c[i]);//求出每个强连通分量的最小cost
        }
        for(int i = 1;i<=n;i++)
        {
            if(Belong[i]!=Belong[a[i]])
            {
                Deg[Belong[i]]++;//如果连个相连的点不在一个强连通分量里面,那么他所属的强连通分量的出度加一
            }
        }
        /*for(int i =1;i<=n;i++)
        {
            cout << "Deg " << i << " : " << Deg[i] << endl;
        }*/
        int ans = 0;
        for(int i = 1;i<=Bcnt;i++) //遍历所有的强连通分量
        {
            //cout << "deg " << i << ":" << Deg[i] << endl;
            if(Deg[i]==0)
            {
                ans += cost[i];//如果该连通分量的出度为一,则比在这个连通分量上放一个捕鼠夹
            }
        }
        cout << ans << endl;
        return 0;
    }
    

    参考文章:

    悠悠呦~,Codeforces 1027D Mouse Hunt (强连通缩点 || DFS+并查集),https://www.cnblogs.com/00isok/p/10394721.html

  • 相关阅读:
    鸡啄米vc++2010系列46(Ribbon界面开发:使用更多控件并为控件添加消息处理函数)
    鸡啄米vc++2010系列45(Ribbon界面开发:为Ribbon Bar添加控件)
    鸡啄米vc++2010系列44(Ribbon界面开发:创建Ribbon样式的应用程序框架)
    鸡啄米vc++2010系列43(MFC常用类:定时器Timer)
    鸡啄米vc++2010系列42(MFC常用类:CTime类和CTimeSpan类)
    鸡啄米vc++2010系列41(MFC常用类:CString类)
    pcl库的配置
    鸡啄米vc++2010系列40(文档、视图和框架:分割窗口)
    鸡啄米vc++2010系列39(文档、视图和框架:各对象之间的关系)
    c 函数传入数组。
  • 原文地址:https://www.cnblogs.com/zhanhonhao/p/11296017.html
Copyright © 2020-2023  润新知