您的位置:首页 > 其它

bzoj4727 [POI2017]Turysta(竞赛图构造哈密顿回路)

2018-01-23 23:41 323 查看

bzoj4727 [POI2017]Turysta

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=4727

题意:

给出一个n个点的有向图,任意两个点之间有且仅一条有向边。对于每个点v,求出从v出发的一条经过点数最多,

且没有重复经过同一个点两次以上的简单路径。

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

表示有向边j->i+1,如果是0,那么表示有向边j<-i+1。

数据范围

2<=n<=2000

题解:

原图给出来是一个竞赛图。

定理

竞赛图一定存在哈密顿路径

竞赛图存在哈密顿回路 充要条件是强连通

那么每个强连通分量里的点每个点都可以走完所有的点。

那么这道题就是竞赛图构造哈密顿回路。

然后拓扑序DP。

关于竞赛图如何构造(以及上述两个定理的证明):

在竞赛图中构造哈密顿路径:



假设我们已经构造出了前i个点的哈密顿路径(最初为1),那么加入i+1个点时,会有三种情况:

1、指向首。

2、被尾指向。

这两种之间就加在首位了。

3、找到中间第一个被i+1指向的点,它前一个点与i+1与它重新连接。

已知强连通竞赛图中的哈密顿路径,如何构造哈密顿回路





首先找到最近的那个指向首的节点i,形成一个环(因为强连通所以一定存在),然后考虑把环扩大,到i+1节点。

1、指向首,就直接扩大即可。

2、指向其中的某个点,走如图的路径形成新环。

3、没有指向环,考虑下一个点。

于是就完成了构造及证明。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N=2005;
int n,x,g

,to

,dp
,low
,dfn
,inc=0,S
,top=0,pl
,cnt=0,q
,du
,pre
,root
,tot=0,V
,nxt
;
bool ins
;
inline int read()
{
int ret=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-'){w=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar();}
return ret*w;
}
void get()
{
int head=V[1]; int tail=V[1];
if(tot==1) {nxt[tail]=tail; return;}
for(int j=2;j<=tot;j++)
{
int i=V[j];
if(g[i][head]){nxt[i]=head; head=i; continue;}
else if(g[tail][i]){nxt[tail]=i; tail=i; continue;}
int x,y; for(x=nxt[head],y=head;x&&!g[i][x];y=x,x=nxt[x]);
nxt[y]=i; nxt[i]=x;
}
tail=head; head=0;
for(int i=nxt[tail];i;i=nxt[i])
{
if(head)
{
for(int p1=head,p2=tail;;p2=p1,p1=nxt[p1])
{
if(g[i][p1])
{
nxt[p2]=nxt[tail];
if(p2!=tail) nxt[tail]=head;
tail=i; head=p1; break;
}
if(p1==tail) break;
}
}
else if(g[i][tail]){head=tail;tail=i;}
}
nxt[tail]=head;
}
void dfs(int u)
{
inc++; dfn[u]=low[u]=inc;
ins[u]=1; S[++top]=u;
for(int v=1;v<=n;v++) if(g[u][v])
{
if(!dfn[v]) {dfs(v); low[u]=min(low[u],low[v]);}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++; root[cnt]=u; tot=0;
while(1)
{
dp[cnt]++;  V[++tot]=S[top];
pl[S[top]]=cnt; ins[S[top]]=0;
top--; if(S[top+1]==u) break;
}
get();
}
}
void print(int x)
{
if(!x){printf("\n");return;}
printf("%d ",x);
for(int i=nxt[x];i!=x;i=nxt[i]) printf("%d ",i);
print(root[pre[pl[x]]]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
for(int j=1;j<=i;j++)
{
x=read();
if(x) g[j][i+1]=1;
else g[i+1][j]=1;
}
for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(g[i][j]&&pl[i]!=pl[j]) to[pl[i]][pl[j]]=1;
for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++) if(i!=j&&to[j][i]) du[i]++;
int lf=1,rg=0; top=0; for(int i=1;i<=cnt;i++) if(du[i]==0) S[++rg]=i;
while(lf<=rg)
{
int x=q[++top]=S[lf]; lf++;
for(int y=1;y<=cnt;y++)
{
if(!to[x][y]||y==x) continue;
du[y]--; if(du[y]==0) S[++rg]=y;
}
}
for(int i=top;i>=1;i--)
{
int x=q[i]; int det=0;
for(int y=1;y<=cnt;y++)
{
if(!to[x][y]||x==y) continue;
if(det<dp[y]){det=dp[y]; pre[x]=y;}
}
dp[x]+=det;
}
for(int i=1;i<=n;i++) {printf("%d ",dp[pl[i]]); print(i);}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: