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
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
3 2 3 4
3 3 4 2
3 4 2 3
HINT
Source
图论
首先竞赛图一定存在哈密顿路径
然后强连通的竞赛图一定存在哈密顿回路
并且竞赛图缩点之后会变成一条链(如果一个点有两条出边,那么那两个对应的点一定有边相连)
然后就是一个dp问题了,具体看代码的注释
对于哈密顿回路的找法,可以看这个博客的图
http://blog.csdn.net/lych_cys/article/details/54633296
另外图下面的第一句话写错了,应该改成如果存在边6->1
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
int n, dfn[MAXN], low[MAXN], scc[MAXN], size[MAXN], num, tim, st[MAXN], top;//tarjan记录的东西
int nxt[MAXN], h[MAXN], m;//nxt表示在当前的scc中的下一个点,h[1-m]是把当前scc中的点提取出来
int nxtscc[MAXN];//nxtscc表示下一个的scc的其中一个点
int dp[MAXN], nxtdp[MAXN];
bool a[MAXN][MAXN], vis[MAXN], can[MAXN], b[MAXN][MAXN];//a是map,b是scc中的map,vis表示是否在之前的环中出现过,can表示是否能到达之前的环上的点
inline void pushin(int x)
{
vis[ x ] = 1;
for( int i = 1 ; i <= m ; i++ ) if( a[ h[ i ] ][ x ] ) can[ h[ i ] ] = 1;
}
inline void getnxt()
{
int head = h[ 1 ], tail = h[ 1 ];//表示路径的首尾
for( int i = 2 ; i <= m ; i++ ) //找一条哈密顿路径
{
int x = h[ i ];
if( a[ x ][ head ] ) { nxt[ x ] = head; head = x; continue; } //如果x->head,则它作为队首
for( int now = head, last = 0 ; ; last = now, now = nxt[ now ] )
{
if( last == tail )//如果之前路径上没有x直接连边到达的点,则x作为路径的末尾
{
nxt[ tail ] = x; tail = x;
break;
}
if( a[ x ][ now ] )//如果x到之前路径上存在last->x->now,把x插在中间
{
nxt[ x ] = now; nxt[ last ] = x;
break;
}
}
}
int end = head; pushin( head );//end表示当前找到的环的末尾,end->head存在一条虚边
while( end ^ tail )
{
int x = nxt[ end ];
if( a[ x ][ head ] ) { end = x; pushin( x ); continue; } //如果x->head,则end->x,x作为end
for( int now = head, last = 0 ; ; last = now, now = nxt[ now ] )
{
if( a[ x ][ now ] ) { nxt[ last ] = x; nxt[ end ] = head; end = x; head = now; pushin( x ); break; } //last->x->now,把x插入
//注意插入的时候要把end给修改掉
if( last == end )//如果环上的点到x的边都指向x,那么找到x后面的一个点使得它向之前环上的点连边,记为newend, 如果没有就不强连通了
//newend指向的点记为y,则y->环上->pre[y]->x->链上->newend->(虚边)y构成新的环
{
int newend, y;
for( int i = 1 ; i <= m ; i++ ) if( !vis[ h[ i ] ] && can[ h[ i ] ] ) { newend = h[ i ]; break; }
for( int i = head ; ; i = nxt[ i ] ) if( a[ newend ][ i ] || i == end ) { y = i; break; }
for( int i = nxt[ end ] ; ; i = nxt[ i ] ) { pushin( i ); if( i == newend ) break; }
nxt[ end ] = head; head = y; end = newend;
for( int now = head ; ; now = nxt[ now ] )
if( nxt[ now ] == y )
{
nxt[ now ] = x;
break;
}
break;
}
}
}
nxt[ tail ] = head;
for( int i = 1 ; i <= m ; i++ ) vis[ h[ i ] ] = can[ h[ i ] ] = 0;
}
inline void dfs(int x)
{
int tmp = 0;
dfn[ x ] = low[ x ] = ++tim;
st[ ++top ] = x;
for( int y = 1 ; y <= n ; y++ ) if( a[ x ][ y ] )
if( !dfn[ y ] ) dfs( y ), low[ x ] = min( low[ x ], low[ y ] );
else if( !scc[ y ] ) low[ x ] = min( low[ x ], dfn[ y ] );
if( dfn[ x ] == low[ x ] )
{
nxtscc[ ++num ] = x;
m = 0;
while( tmp ^ x )
{
tmp = st[ top-- ];
size[ scc[ tmp ] = num ]++;
h[ ++m ] = tmp;
}
getnxt();
}
}
inline int getdp(int x)
{
if( dp[ x ] ) return dp[ x ];
//printf( "%d\n", x );
for( int i = 1 ; i <= num ; i++ ) if( b[ x ][ i ] && x != i )
{
int g = getdp( i );
if( dp[ x ] < g ) { dp[ x ] = g; nxtdp[ x ] = i; }
}
dp[ x ] += size[ x ];
//printf( "dp[%d]=%d\n", x, dp[ x ] );
return dp[ x ];
}
inline void solve(int x)
{
if( !x ) return ;
printf( " %d", x );
for( int i = nxt[ x ] ; i ^ x ; i = nxt[ i ] ) printf( " %d", i );
solve( nxtscc[ nxtdp[ scc[ x ] ] ] );
}
int main()
{
scanf( "%d", &n );
for( int j = 2 ; j <= n ; j++ )
for( int i = 1 ; i < j ; i++ )
scanf( "%d", &a[ i ][ j ] ), a[ j ][ i ] = a[ i ][ j ] ^ 1;
for( int i = 1 ; i <= n ; i++ ) if( !dfn[ i ] ) dfs( i );
//for( int i = 1 ; i <= n ; i++ ) printf( "%d ", nxt[ i ] );
for( int i = 1 ; i <= n ; i++ )
for( int j = 1 ; j <= n ; j++ )
if( a[ i ][ j ] ) b[ scc[ i ] ][ scc[ j ] ] |= 1;
//for( int i = 1 ; i <= num ; i++, putchar( 10 ) )
//for( int j = 1 ; j <= num ; j++ )
//printf( "%d ", b[ i ][ j ] );
for( int i = 1 ; i <= num ; i++) getdp( i );
//for( int i = 1 ; i <= n ; i++ ) printf( "scc[%d]=%d size=%d dp=%d\n", i, scc[ i ], size[ scc[ i ] ], dp[ scc[ i ] ] );
for( int i = 1 ; i <= n ; i++ )
printf( "%d", dp[ scc[ i ] ] ), solve( i ), putchar( 10 );
}