题解:
这题算是竞赛图相关知识的简单运用了吧。
完成此题,你需要知道:
1、竞赛图都存在一条哈密顿路径。
这个比较简单,反证法,假如没有,设一条最长路径为
a
1
−
>
a
2
.
.
.
a
n
−
1
−
>
a
n
a_1->a_2...a_{n-1}->a_n
a1−>a2...an−1−>an,考虑一个这条路径之外的点
p
p
p:
1、
p
p
p连向
a
1
a_1
a1,那么路径长度
+
1
+1
+1。
2、
a
n
a_n
an连向
p
p
p,路径长度
+
1
+1
+1。
否则,一定是这样的:
不难发现,不论怎么给红色的边定向,都会存在
a
i
−
>
p
a_i->p
ai−>p,
p
−
>
a
i
+
1
p->a_{i+1}
p−>ai+1,这样路径的长度
+
1
+1
+1。这样完成了证明,实际上也是竞赛图哈密顿路径的构造方法。
2、竞赛图存在哈密顿回路当且仅当图强连通。
同样,这个东西的证明也是可以在构造的过程中完成的。假设我们现在已经得到了一条哈密顿路径,考虑怎么把它变成哈密顿回路(假设其存在,即图强连通)。设竞赛图有
n
n
n个点,哈密顿路径为
a
1
−
>
a
2
.
.
.
a
n
−
1
−
>
a
n
a_1->a_2...a_{n-1}->a_n
a1−>a2...an−1−>an,先找到第一个连回
a
1
a_1
a1的点
a
x
a_x
ax,然后考虑
a
x
+
1
a_{x+1}
ax+1如何让这个环扩大:
1、
a
x
+
1
−
>
a
1
a_{x+1}->a_1
ax+1−>a1,直接可以扩大。
2、
a
y
−
>
a
x
+
1
−
>
a
y
+
1
a_y->a_{x+1}->a_{y+1}
ay−>ax+1−>ay+1(
y
y
y在之前的环上),也可以直接扩大。
否则即环上所有的点与
a
x
+
1
a_{x+1}
ax+1的边都是连向它的,这时考虑后面(也就是还未加入到环中的链上的点)第一个可以连回环上的点
a
p
a_p
ap(这样的点一定存在否则图则不强连通),设
a
p
a_p
ap连向环上的
a
q
a_q
aq,那么环可以扩成这样:
a
q
−
>
绕
环
−
>
a
q
−
1
−
>
a
x
+
1
−
>
链
上
其
它
点
(
可
能
没
有
)
—
>
a
p
−
>
a
q
a_q->绕环->a_{q-1}->a_{x+1}->链上其它点(可能没有)—>a_p->a_q
aq−>绕环−>aq−1−>ax+1−>链上其它点(可能没有)—>ap−>aq。不断重复此过程,即可构造出一条哈密顿回路。
说了这么多,回到这题就很简单了。先Tarjan缩点,竞赛图缩点后一定是一条链的样子,那么最优的方案一定是每个强连通分量都用这个强连通分量的哈密顿回路走完,然后再到下一个强连通分量,求出每个强连通分量后求出它的哈密顿回路即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=2010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,mp[Maxn][Maxn];
int low[Maxn],dfn[Maxn],id=0,sta[Maxn],top=0;bool in[Maxn];
int bel[Maxn],cnt=0,sz[Maxn];
vector<int>scc[Maxn];
int nxt[Maxn];
bool vis[Maxn],can[Maxn];
void push(int x)
{
vis[x]=true;
for(int i=0;i<scc[cnt].size();i++)
if(mp[scc[cnt][i]][x])can[scc[cnt][i]]=true;
}
void work()
{
int st=scc[cnt][0],ed=scc[cnt][0];
for(int i=1;i<scc[cnt].size();i++)
{
int x=scc[cnt][i];
if(mp[ed][x]){nxt[ed]=x;ed=x;continue;}
if(mp[x][st]){nxt[x]=st;st=x;continue;}
for(int y=st;;y=nxt[y])
if(mp[y][x]&&mp[x][nxt[y]])
{
nxt[x]=nxt[y];nxt[y]=x;
break;
}
}
int end=st;push(st);
while(end!=ed)
{
int x=nxt[end],last=0;
if(mp[x][st]){end=x;push(x);continue;}
for(int y=st;;y=nxt[y])
{
if(mp[x][y]){nxt[last]=x;nxt[end]=st;end=x;st=y;push(x);break;}
//y->环->pre[y]->x->y
if(last==end)
{
int newend,t;
for(int i=0;i<scc[cnt].size();i++)if(!vis[scc[cnt][i]]&&can[scc[cnt][i]]){newend=scc[cnt][i];break;}
for(int i=st;;i=nxt[i])if(mp[newend][i]){t=i;break;}
for(int i=nxt[end];;i=nxt[i]){push(i);if(i==newend)break;}
nxt[end]=st;st=t;end=newend;
for(int i=st;;i=nxt[i])
if(nxt[i]==t){nxt[i]=x;break;}
break;
}
last=y;
//t->环->pre[t]->x->链->newend->t
}
}
nxt[ed]=st;
}
void Tarjan(int x)
{
dfn[x]=low[x]=++id;sta[++top]=x;in[x]=true;
for(int y=1;y<=n;y++)
{
if(x==y||!mp[x][y])continue;
if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
else if(in[y])low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
int t;cnt++;sz[cnt]=0;
do
{
sz[cnt]++;
t=sta[top--];
scc[cnt].push_back(t);
bel[t]=cnt;
in[t]=false;
}while(t!=x);
work();
}
}
int Nxt[Maxn],deg[Maxn];bool Mp[Maxn][Maxn];
int St,Ed;
void dfs(int x)
{
if(x==Ed)return;
dfs(Nxt[x]);sz[x]+=sz[Nxt[x]];
}
void print(int p,int o)
{
for(int x=p;;x=nxt[x])
{
printf("%d ",x);
if(nxt[x]==p)break;
}
if(o==Ed)return;
print(scc[Nxt[o]][0],Nxt[o]);
}
int main()
{
n=read();
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++)
mp[j][i]=read(),mp[i][j]=(!mp[j][i]);
for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(bel[i]!=bel[j]&&mp[i][j]&&!Mp[bel[i]][bel[j]])deg[bel[j]]++,Mp[bel[i]][bel[j]]=true;
queue<int>q;
for(int i=1;i<=cnt;i++)if(!deg[i])q.push(St=i);
while(!q.empty())
{
int x=q.front();q.pop();Ed=x;
for(int i=1;i<=cnt;i++)
if(Mp[x][i])
{
deg[i]--;
if(!deg[i])Nxt[x]=i,q.push(i);
}
}
dfs(St);
for(int i=1;i<=n;i++)printf("%d ",sz[bel[i]]),print(i,bel[i]),puts("");
}