【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("\n");
}

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; 
}
posted @ 2018-07-07 10:42  Maxwei_wzj  阅读(119)  评论(0编辑  收藏  举报