4727: [POI2017]Turysta

4727: [POI2017]Turysta

Time Limit: 20 Sec Memory Limit: 128 MBSec Special Judge
Submit: 138 Solved: 47
[Submit][Status][Discuss]
Description

给出一个n个点的有向图,任意两个点之间有且仅一条有向边。对于每个点v,求出从v出发的一条经过点数最多,
且没有重复经过同一个点两次以上的简单路径。
Input

第一行包含一个正整数n(2<=n<=2000),表示点数。接下来n-1行,其中的第i行有i-1个数,如果第j个数是1,那么
表示有向边j->i+1,如果是0,那么表示有向边j<-i+1。
Output

输出n行,第i行首先包含一个正整数k,表示从i点出发的最优路径所经过的点数,接下来k个正整数,依次表示路
径上的每个点。若有多组最优解,输出任意一组。
Sample Input

4

1

1 1

1 0 1
Sample Output

4 1 2 3 4

3 2 3 4

3 3 4 2

3 4 2 3
HINT

Source

鸣谢Claris上传

[Submit][Status][Discuss]


对于一张任意两个点间有且仅有一条有向路径的图,我们称之为竞赛图
显然,原题给出的图就是一张竞赛图
对于一张竞赛图,我们有如下两个性质:

  1. 对于竞赛图的任意子图,总能找到一条哈密顿路径
  2. 对于竞赛图的任意一个强连通分量,总能找到一条哈密顿回路

可以采用构造法证明这两个性质

证明1:

假设对于子图 G ,一共有N个节点
下面用数学归纳法证明该性质
pre[x] 为路径中节点 x 的前一个节点,nex[x]为后一个
k=1 时,显然找到了前 1 个节点的哈密顿路径
假设对于前k个节点,已经找到一条哈密顿路径,记 s 为起点,t为终点
若存在边 (k+1,s) ,那么让第 k+1 个点成为新路径的起点就行了
若存在边 (t,k+1) ,那么让第 k+1 个点成为新路径的终点就行了
否则,当前一定存在边 (s,k+1) 以及边 (k+1,t) ,因为这两条边方向不同
所以在当前哈密顿回路中一定存在一个点 x ,使得存在边(x,k+1) (k+1,nex[x])
那么只需要将边 (x,nex[x]) 删掉,把 k+1 替进来就行了
于是总是能将第 k+1 个点扔进当前哈密顿回路,证明完毕

证明2:

假设对于强连通分量 G ,一共有N个节点
还是用数学归纳法证明该性质
对于 G ,先按照之前的方法预处理出它的任意一条哈密顿路径
s为路径节点, t 为路径终点
按照从s出发走向 t 的顺序逐个将节点插入哈密顿回路
k=1时,显然找到了前 1 个节点的哈密顿回路
假设对于前k个节点,已经找到一条哈密顿回路
pre[x] 为回路中节点 x 的前一个节点,nex[x]为后一个
若前 k 个节点组成的哈密顿回路中存在点x,使得存在边 (x,k+1) (k+1,nex[x])
那么删掉边 (x,nex[x]) ,把 k+1 替进来就行了
否则,对于回路中的任意点 x ,均存在边(x,k+1)
每一次,在原哈密顿路径的基础上后移一个节点,若存在节点 y 含有一条边指向x(xk)
那么,就可以删掉边 (x,nex[x]) 用路径 xk+1ynex[x] 替换
因为子图 G 是原图的一个强连通分量,因此,节点y是一定存在的


综合以上两个性质
对于原图,先用 tarjan 算法找出所有强连通分量
这样原图就变成了一个 DAG ,对于每个节点,处理出一条哈密顿回路
于是经过每个节点的时候总是能把它内部节点全部走一遍过去的
剩下的问题就是 DAG dp 求最长路径了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;

const int maxn = 2002;

int n,m,tot,dfs_clock,N,A[maxn],B[maxn],bel[maxn],Nex[maxn],H[maxn][maxn]
    ,siz[maxn],Max[maxn],nex[maxn],du[maxn],dfn[maxn],low[maxn];
bool G[maxn][maxn];

queue <int> Q;
stack <int> s;
vector <int> v[maxn],g[maxn];

inline void Build_Circle()
{
    Nex[B[1]] = B[1];
    for (int i = 2; i <= N; i++)
    {
        bool pass = 0; int now = B[i];
        for (int x = B[1]; ; x = Nex[x])
        {
            if (G[x][now] && G[now][Nex[x]])
            {
                Nex[now] = Nex[x];
                Nex[x] = now; pass = 1; break;
            }
            if (Nex[x] == B[1]) break;
        }
        if (pass) continue;
        for (int j = i + 1; ; j++)
        {
            int Num = 0;
            for (int x = B[1]; ; x = Nex[x])
            {
                if (G[B[j]][x]) {Num = x; break;}
                if (Nex[x] == B[1]) break;
            }
            if (!Num) continue;
            for (int x = B[1]; ; x = Nex[x])
                if (Nex[x] == Num) {Nex[x] = now; break;}
            Nex[B[j]] = Num; i = j; break;  
        }
    }
}

inline void Build_Path()
{
    int ft,lt,tp = 0; ft = lt = A[1];
    for (int i = 2; i <= N; i++)
    {
        int now = A[i];
        if (G[now][ft]) Nex[now] = ft,ft = now;
        else if (G[lt][now]) Nex[lt] = now,lt = now;
        else
        {
            for (int x = ft; ; x = Nex[x])
                if (G[x][now] && G[now][Nex[x]])
                {
                    Nex[now] = Nex[x];
                    Nex[x] = now; break;
                }
        }
    }
    for (int x = ft; x; x = Nex[x]) B[++tp] = x; Build_Circle();
}

inline void Dfs(int x)
{
    dfn[x] = low[x] = ++dfs_clock; s.push(x);
    for (int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if (!dfn[to]) Dfs(to),low[x] = min(low[x],low[to]);
        else if (!bel[to]) low[x] = min(low[x],dfn[to]);
    }
    if (dfn[x] == low[x])
    {
        ++tot;
        for (;;)
        {
            int k = s.top(); s.pop(); ++siz[tot];
            A[++N] = k; bel[k] = tot; if (k == x) break;
        }
        Build_Path(); N = 0;
    }
}

char ch[20];
inline void Print(int x,char c)
{
    int len = 0;
    while (x) ch[++len] = x % 10,x /= 10;
    for (int i = len; i; i--) putchar(ch[i] + '0'); putchar(c);
}

inline void Solve(int k)
{
    int cur = k;
    for (;;)
    {
        for (int x = k; ; x = Nex[x])
        {
            A[++N] = x;
            if (Nex[x] == k) {k = x; break;}
        }
        if (!nex[bel[k]]) break; k = H[k][nex[bel[k]]];
    }
    Print(N,' '); for (int i = 1; i < N; i++) Print(A[i],' ');
    Print(A[N],'\n'); N = 0;
}

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
        freopen("test.txt","w",stdout);
    #endif

    n = getint();
    for (int i = 2; i <= n; i++)
        for (int j = 1; j < i; j++)
        {
            int x = getint();
            if (x) v[j].push_back(i),G[j][i] = 1;
            else v[i].push_back(j),G[i][j] = 1;
        }
    for (int i = 1; i <= n; i++)
        if (!dfn[i]) Dfs(i);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < v[i].size(); j++)
        {
            int to = v[i][j];
            if (bel[i] == bel[to]) continue;
            g[bel[to]].push_back(bel[i]);
            H[i][bel[to]] = to; ++du[bel[i]];
        }
    for (int i = 1; i <= tot; i++)
        if (!du[i]) Q.push(i);
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop(); Max[k] += siz[k];
        for (int i = 0; i < g[k].size(); i++)
        {
            int to = g[k][i]; --du[to];
            if (Max[k] > Max[to])
                Max[to] = Max[k],nex[to] = k;
            if (!du[to]) Q.push(to);
        }
    }
    for (int i = 1; i <= n; i++) Solve(i);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值