• 【BZOJ4727】Turysta(POI2017)-构造+链表+SCC


    测试地址:Turysta
    题目大意:给定一张竞赛图,要求求出从每个点出发的,经过点数最多的一条简单路径,输出方案。
    做法:本题需要用到构造+链表+SCC。
    首先可以证明一张竞赛图必有一条哈密顿路径(反证法),又可以证明一张强连通的竞赛图必有一条哈密顿回路(这个暂时不知道怎么证,不过看了下面的构造方法差不多也能明白)。我们先来找出原图的哈密顿路径。
    竞赛图的任意导出子图一定都是竞赛图,所以我们可以递推进行:假设我们已经求出包含1i的哈密顿路径,那么对于点i+1
    如果它指向前面求出的哈密顿路径的头,或者被前面求出的哈密顿路径的尾指向,就直接接上去。
    否则,我们至少知道点i+1被前面求出的哈密顿路径的头指向,并指向前面求出的哈密顿路径的尾。因此在这条路径中一定存在相邻的两个点,满足前面的点指向i+1,后面的点被i+1指向,那么只要把i+1接在两点之间即可。这样的递推用链表维护是O(n2)的。
    求出了哈密顿路径,我们能断言:这张图的任意强连通分量必定是这条路径上的一个连续段,并且两个不同强连通分量之间的边的联系,只有可能是从路径中靠前的连向路径中靠后的。换句人话来说,就是对竞赛图SCC缩点后得到的一定是一条链。
    前面我们说了,一个强连通竞赛图中必然含有哈密顿回路,那么现在问题就变成,如何求出这样的哈密顿回路,求出之后我们就可以很方便地构造出题目中所求的最佳路径了:首先按当前强连通分量的哈密顿回路走,然后贪心走到下一个强连通分量,再按哈密顿回路走……
    下面我们给出有哈密顿路的基础上,求哈密顿回路的算法。我们假设已经求出哈密顿路上前i个点的哈密顿回路,而后面接着一条链直到第j个点,那么:
    如果环中存在一个位置使得它被j指向,而它在环上的前一个点指向从环中接出的那条链的头,那么就可以把这条链接到环中。
    否则,跳过当前点,考虑j+1
    于是我们就解决了这一题,时间复杂度为O(n2)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,pre[2010]={0},nxt[2010]={0},head=1,tail=1;
    int low[2010],dfn[2010],tim=0,belong[2010],totscc=0;
    int st[2010],top=0;
    int looppre[2010],loopnxt[2010],nxtscc[2010];
    bool g[2010][2010]={0},vis[2010]={0},inst[2010]={0};
    
    void insert(int x,int y)
    {
        pre[nxt[x]]=y;
        nxt[y]=nxt[x];
        nxt[x]=y;
        pre[y]=x;
    }
    
    void calc_path()
    {
        for(int i=2;i<=n;i++)
        {
            if (g[i][head])
            {
                nxt[i]=head;
                pre[head]=i;
                head=i;
                continue;
            }
            if (g[tail][i])
            {
                nxt[tail]=i;
                pre[i]=tail;
                tail=i;
                continue;
            }
            int x=head;
            while(!g[i][nxt[x]]) x=nxt[x];
            insert(x,i);
        }
    }
    
    void tarjan(int v)
    {
        vis[v]=1;
        dfn[v]=low[v]=++tim;
        st[++top]=v;
        inst[v]=1;
        int now=top;
        for(int i=1;i<=n;i++)
            if (g[v][i])
            {
                if (!vis[i])
                {
                    tarjan(i);
                    low[v]=min(low[v],low[i]);
                }
                else if (inst[i]) low[v]=min(low[v],dfn[i]);
            }
        if (low[v]>=dfn[v])
        {
            ++totscc;
            for(int i=now;i<=top;i++)
            {
                belong[st[i]]=totscc;
                inst[st[i]]=0;
            }
            top=now-1;
        }
    }
    
    void calc_loop(int &v,int c)
    {
        int x=v;
        if (!nxt[x]||belong[nxt[x]]!=c)
        {
            looppre[x]=loopnxt[x]=x;
            nxtscc[x]=nxt[x];
            v=nxt[x];
            return;
        }
    
        while(x&&belong[x]==c)
        {
            looppre[x]=pre[x];
            if (g[x][v])
            {
                loopnxt[x]=v;
                looppre[v]=x;
                break;
            }
            loopnxt[x]=nxt[x];
            x=nxt[x];
        }
    
        x=nxt[x];
        int now=x;
        while(x&&belong[x]==c)
        {
            int p=v;
            bool flag=0;
            do
            {
                if (g[p][now]&&g[x][loopnxt[p]])
                {
                    loopnxt[x]=loopnxt[p];
                    looppre[loopnxt[p]]=x;
                    looppre[now]=p;
                    loopnxt[p]=now;
                    flag=1;
                    break;
                }
                p=loopnxt[p];
            }while(p!=v);
            if (flag) now=nxt[x];
            else loopnxt[x]=nxt[x],looppre[x]=pre[x];
            x=nxt[x];
        }
        int tmp=v;
        while(tmp&&belong[tmp]==c)
            nxtscc[tmp]=x,tmp=nxt[tmp];
        v=x;
    }
    
    void calc_ans(int i,bool type)
    {
        int x=i,cnt=0;
        while(x)
        {
            int v=x;
            do
            {
                if (type) printf("%d ",v);
                else cnt++;
                v=loopnxt[v];
            }while(v!=x);
            x=nxtscc[x];
        }
        if (!type) printf("%d ",cnt);
        else printf("
    ");
    }
    
    void work()
    {
        int x=head;
        while(x) calc_loop(x,belong[x]);
        for(int i=1;i<=n;i++)
        {
            calc_ans(i,0);
            calc_ans(i,1);
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++)
            for(int j=1;j<=i;j++)
            {
                int x;
                scanf("%d",&x);
                if (x) g[j][i+1]=1;
                else g[i+1][j]=1;
            }
    
        calc_path();
        tarjan(head);
        work();
    
        return 0; 
    }
  • 相关阅读:
    Wepy 格式化和语法高亮(vscode)
    TypeError: Cannot read property '_wrapper' of undefined
    Vue 自定义事件传参
    Uncaught (in promise) undefined
    微信小程序 获取用户昵称、头像
    微信小程序scroll-view去除滚动条
    微信小程序从子页面退回父页面时的数据传递 wx.navigateBack()
    wx: wx.showModal 回调函数中调用自定义方法
    c#后端 小程序上传图片
    小程序配置,通用域名配置文件
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793296.html
Copyright © 2020-2023  润新知