4727: [POI2017]Turysta 竞赛图相关

题解:

这题算是竞赛图相关知识的简单运用了吧。
完成此题,你需要知道:

1、竞赛图都存在一条哈密顿路径。

这个比较简单,反证法,假如没有,设一条最长路径为 a 1 − > a 2 . . . a n − 1 − > a n a_1->a_2...a_{n-1}->a_n a1>a2...an1>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...an1>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>>aq1>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("");
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值