博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【LOJ6043】「雅礼集训 2017 Day7」蛐蛐国的修墙方案(搜索技巧题)
阅读量:4668 次
发布时间:2019-06-09

本文共 2405 字,大约阅读时间需要 8 分钟。

大致题意: 给你一个长度为\(n\)的排列\(p\),要求构造一个合法的括号序列,使得如果第\(i\)个位置是左括号,则第\(p_i\)个位置一定是右括号。

暴搜

很容易想出一个暴搜。

即对于每一个没有确定的位置\(x\),无非有两种情况:

  • 选左括号。前提是\(p_x\)没被选过或者\(p_x\)为右括号,然后标记第\(p_x\)位为右括号。
  • 选右括号。我们可以开一个变量\(v\)来记录左括号个数\(-\)有括号个数,则选右括号的前提是\(v>0\)。(因为要是一个合法的括号序列)

最后判断\(v\)是否恰好等于\(0\)即可。

代码如下:

#include
#define Tp template
#define Ts template
#define Reg register#define RI Reg int#define Con const#define CI Con int&#define I inline#define W while#define N 100using namespace std;int n,a[N+5];char s[N+5];I void dfs(CI x,CI v)//x记录当前位置,v记录左括号个数减右括号个数{ if(x>n) return (void)(!v&&(puts(s+1),exit(0),0));//如果x大于n且v恰好为0,则输出答案,退出程序 if(s[x])//如果已经确定 { if(s[x]^')') return dfs(x+1,v+1);//如果是左括号,搜索下一位 if(v) return dfs(x+1,v-1);return;//如果是右括号且v大于0,搜索下一位 } if(a[x]>x||s[a[x]]^'(') s[x]='(',s[a[x]]=')',dfs(x+1,v+1),s[x]=s[a[x]]='\0';//选左括号 if(v) s[x]=')',dfs(x+1,v-1),s[x]='\0';//选右括号}int main(){ RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);//读入 return dfs(1,0),0;}

所以这样就有\(81\)分了。

如果你是常数之神,说不定能直接过。

观察性质

接下来,我们要观察一下这个题目中的一个性质。

注意到它保证有解,再结合题意,如果我们把\(i->p_i\)看成一条边,则原序列可以看成若干个环,而这些环一定都是偶环(不然就无解了)。

而对于一个偶环,我们对它的选择必然是左括号与右括号相交替的。

也就是说,对于一个偶环,只有两种选择方法,似乎暴枚即可。

然而,要特判环长为\(2\)的情况(不然会像我第一次那样只有\(86\)分——也就比暴搜多\(5\)分),贪心一下即可发现肯定是左边那位选左括号,右边那位选右括号。

这样就能过了。

关于时间复杂度有严谨的证明:

考虑我们特判了环长为\(2\)的情况,也就是说环长至少为\(4\)

则最多\(100÷4=25\)个环,而\(2^{25}\)稳过。

代码

#include
#define Tp template
#define Ts template
#define Reg register#define RI Reg int#define Con const#define CI Con int&#define I inline#define W while#define N 100using namespace std;int n,tot,a[N+5],v[N+5];char s[N+5];vector
f[N+5];I void Check()//验证是否为合法括号序列{ for(RI i=1,t=0;i<=n;++i) if((t+=(s[i]^')'?1:-1))<0) return;//若出现不合法,直接退出函数 puts(s+1),exit(0);//合法则输出答案,退出程序}I void dfs(CI x)//搜索,判断第i个环的填法{ if(x>tot) return Check();RI i,sz=f[x].size(); if(!(sz^2)) return s[f[x][0]]='(',s[f[x][1]]=')',dfs(x+1);//特判环长为2的情况 for(i=0;i^sz;++i) s[f[x][i]]=(i&1?'(':')');dfs(x+1);//环中必然是左右括号交替 for(i=0;i^sz;++i) s[f[x][i]]=(i&1?')':'(');dfs(x+1);//枚举另一种情况}int main(){ RI i,x;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);//读入 for(i=1;i<=n;++i) if(!v[x=i])//如果没访问过 {++tot;W(!v[x]) v[x]=1,f[tot].push_back(x),x=a[x];}//找环 return dfs(1),0;//搜索}

转载于:https://www.cnblogs.com/chenxiaoran666/p/LOJ6043.html

你可能感兴趣的文章
Linux笔记(开机自动将kerne log保存到SD卡中)
查看>>
Ajax提交数据判断员工编号是否存在,及自动填充与员工编号所对应的员工姓名。...
查看>>
CodeForces 689E (离散化+逆元+组合)
查看>>
pycharm 右键无法显示unittest框架&&解决右键只有unittest 运行如何取消右键显示进行普通run...
查看>>
jQuery的选择器
查看>>
Shell 概述、截取字符操作等
查看>>
CTF/web
查看>>
第五章上 首次登陆
查看>>
第5堂:看到词句就会读-上
查看>>
Phpcms V9全站伪静态设置方法
查看>>
POJ 2176 Folding(区间DP)
查看>>
Dynamic Clock in Terminal.
查看>>
C# 中的委托和事件
查看>>
SHT30 Linux标准 i2c-dev 读取程序
查看>>
wpf TabControl控件的用法
查看>>
centos7忘记密码处理办法
查看>>
正确停掉 expdp 或 impdp
查看>>
Image Captioning代码复现
查看>>
UE4 打包C++项目到win32平台报错 could not find mspdbcore.dll
查看>>
sed系列:行或者模式匹配删除特定行
查看>>