• 【LOJ6043】「雅礼集训 2017 Day7」蛐蛐国的修墙方案(搜索技巧题)


    点此看题面

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

    暴搜

    很容易想出一个暴搜。

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

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

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

    代码如下:

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    using 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]]='';//选左括号
    	if(v) s[x]=')',dfs(x+1,v-1),s[x]='';//选右括号
    }
    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<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    using namespace std;
    int n,tot,a[N+5],v[N+5];char s[N+5];vector<int> 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;//搜索
    }
    
  • 相关阅读:
    mysql一些基本操作
    redis的简单使用2
    redis的基本conf
    redis的简单使用
    $.ajax()方法详解(转)
    流行的9个Java框架介绍: 优点、缺点等等
    数据库查询,显示为树形结构(easyui+SSM)
    JAVA-汉字转换成汉语拼音(pinyin4j-2.5.0-sources.jar)
    Jquery EasyUI 中ValidateBox验证框使用讲解(转)
    EasyUI验证扩展
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ6043.html
Copyright © 2020-2023  润新知