题意
给一个长度为n的排列,要求构造一个长度为n的合法括号序,要求满足,构造一张n个点的图,当且仅当第i个位置是左括号时,点i向点p[i]连边,最后形成的图必须满足每个点的度数均为一。
n≤100
n
≤
100
分析
我们在每一个i和p[i]之间连一条双向变,不难发现一条边的两个端点必然恰好有一个是左括号。
是不是看上去就像一道noip普及组naive千里送温暖做好事不求回报easy弱智sb无聊简直不能再水的超级裸题,只要对每个连通分量搜索一下就能过了。
注意适当加剪枝,比如说大小为2的可以直接贪心还有先搜较大的连通块之类的。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=105;
int n,cnt,last[N],ans[N],a[N][N],tot,rk[N];
bool vis[N];
struct edge{int to,next;}e[N*2];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void pre(int x)
{
vis[x]=1;a[tot][++a[tot][0]]=x;
for (int i=last[x];i;i=e[i].next)
if (!vis[e[i].to]) pre(e[i].to);
}
bool dfs(int x)
{
if (x>tot) return 1;
int y=rk[x];
for (int i=1;i<=a[y][0];i++) ans[a[y][i]]=i&1;
int s=0;
for (int i=1;i<=n;i++)
{
s+=!ans[i]?1:-1;
if (s<0) break;
}
if (s>=0&&dfs(x+1)) return 1;
for (int i=1;i<=a[y][0];i++) ans[a[y][i]]=(i&1)^1;
s=0;
for (int i=1;i<=n;i++)
{
s+=!ans[i]?1:-1;
if (s<0) break;
}
if (s>=0&&dfs(x+1)) return 1;
for (int i=1;i<=a[y][0];i++) ans[a[y][i]]=0;
return 0;
}
bool cmp(int x,int y)
{
return a[x][0]<a[y][0];
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
addedge(x,i);
}
for (int i=1;i<=n;i++)
if (!vis[i])
{
tot++;a[tot][0]=0;pre(i);
if (a[tot][0]==2) ans[std::max(a[tot][1],a[tot][2])]=1,tot--;
}
for (int i=1;i<=tot;i++) rk[i]=i;
std::sort(rk+1,rk+tot+1,cmp);
dfs(1);
for (int i=1;i<=n;i++) putchar(!ans[i]?'(':')');
return 0;
}