BZOJ4727: [POI2017]Turysta

来源:互联网 发布:linux怎么读音是什么 编辑:程序博客网 时间:2024/06/10 02:04

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

图论
首先竞赛图一定存在哈密顿路径
然后强连通的竞赛图一定存在哈密顿回路
并且竞赛图缩点之后会变成一条链(如果一个点有两条出边,那么那两个对应的点一定有边相连)
然后就是一个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作为endfor( 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 );}


0 0
原创粉丝点击