[JZOJ5029]围墙

该博客介绍了如何解决[JZOJ5029]围墙问题,通过分析题目条件,得出将排列转化为互不连通的简单环,并利用搜索策略确定每个环的状态。博主首先特判了大小为2的环,然后优化搜索复杂度,最终实现了一个O(n/2)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定一个长度为n的排列{Pi}(i,Pii),要求构造一种合法的括号序列,满足:
构造一个n个点的图,当第i个位置是左括号时,存在边(i,Pi),最后形成的图必须满足每个点度数为1
输出任意一种方案即可,数据保证有解。

1n100


题目分析

显然我们先将所有(i,Pi)连上会形成若干个互不连通的简单环,一个简单环只要确定了一个点是左还是右就能确定整个环的状态。那么我们显然可以搜索加判断,因为每个环最小大小是2,所以复杂度是O(2n/2)
考虑怎么优化,可以发现大小为2的环其实可以直接根据先后顺序确定填什么。那么我们直接特判掉大小为2的环,剩下的环最小大小是4(奇数显然无解),于是复杂度降为O(2n/4)。是不是很机智?


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>

using namespace std;

const int N=105;

int P[N],fa[N],rank[N];
bool seq[N],cet[N];
int n;

int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}

inline int merge(int x,int y)
{
    if (rank[x]>rank[y]) swap(x,y);
    fa[x]=y,rank[y]+=rank[x]==rank[y];
}

inline void go(int x){for (int y=P[x],c=seq[x];x!=y;c=seq[y]=c^1,cet[y]^=1,y=P[y]);}

bool dfs(int x,int sum)
{
    if (sum<0) return 0;
    if (x==n+1) return !sum;
    if (!cet[x])
    {
        /*if (seq[x]=0,go(x),dfs(x+1,sum+(seq[x]?-1:1))) return 1;
        go(x);
        if (P[P[x]]==x) return 0;
        if (seq[x]=1,go(x),dfs(x+1,sum+(seq[x]?-1:1))) return 1;
        return go(x),0;*/
        for (int c=0,y,z,cnt;c<2;++c)
        {
            for (cnt=1,z=seq[x]=c,y=P[x];x!=y;z=seq[y]=z^1,cet[y]=1,y=P[y],++cnt);
            if (dfs(x+1,sum+(seq[x]?-1:1))) return 1;
            if (cnt==2) return 0;
        }
        for (int y=P[x];x!=y;y=P[y],cet[y]=0);
        return 0;
    }else return dfs(x+1,sum+(seq[x]?-1:1));
}

int main()
{
    freopen("wall.in","r",stdin),freopen("wall.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;++i) fa[i]=i;
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&P[i]);
        int fx=getfather(i),fy=getfather(P[i]);
        if (fx!=fy) merge(fx,fy);
    }
    dfs(1,0);
    for (int i=1;i<=n;++i) putchar(seq[i]?')':'(');
    printf("\n");
    fclose(stdin),fclose(stdout);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值