#6043. 「雅礼集训 2017 Day7」蛐蛐国的修墙方案
内存限制:1024 MiB
时间限制:2000 ms
标准输入输出
题目类型:传统
评测方式:Special Judge
上传者: 匿名
题目描述
输入格式
输出格式
样例
数据范围与提示
n<=100,看上去不能搜。
优化一下,发现这是由一些环形的关系组成的(因为题目保证有解,所以一定是一些偶环,自然也没有单个点的存在)
而每一个环形关系中,只要确定了一个就可以确定全部,那么最坏情况是环长度都为2,
计算量在2^50,不能接受
同时发现,长度为2时可以贪心,前面的为(,后面的为)
(合法的括号序列等价于左括号和右括号数量相等且在任意一个前缀中“(“数量不少于“)“)
所以长度为2的环反而成了我们的优势(可以特判)这也是绑点的原因吧,不然没法同时卡找环爆搜和贪心加爆搜,就会被骗很多分(出题人良心)
然后两种结合一下,那么最坏情况是环长度都为4,2^25
你还可以加点剪枝(但是一般只能减到2^23左右,就最后几个剪剪)
也算一种打暴力的思路吧;
另外,DFS搜索时,最后一种情况处理之后也应注意是否还原,如果这个选择和后面的选择相关,一定要还原!
如果无关,比如你写的是那种搜出一种完整方案再最后统一检查答案的(也就是说放弃了启发式的剪枝),随便吧。
ACcode:
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 205
using namespace std;
char ANS[3]=")(";
int loop[maxn][maxn],cnt,len[maxn],bel[maxn];
int n;
int Prev[maxn*2],to[maxn*2],info[maxn],cnt_e;
int lp[maxn],tot;
bool vis[maxn];
int ans[maxn];
void Node(int u,int v){
cnt_e++;
Prev[cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;
}
void dfs(int now,int ff){
if(vis[now]) return;
vis[now]=1;
lp[tot++]=now;
for(int i=info[now];i;i=Prev[i])
if(ff!=to[i])
dfs(to[i],now);
}
void ser(int now){
tot=0;
dfs(now,-1);
if(tot==2)
ans[min(lp[0],lp[1])]=1,ans[max(lp[0],lp[1])]=-1;
else{
len[++cnt]=tot;
for(int i=0;i<tot;i++)
loop[cnt][i]=lp[i],bel[lp[i]]=cnt;
}
}
bool flag=0;
void Dfs(int now,int sum){
if(sum<0) return;
if(now==n+1){
for(int i=1;i<=n;i++)
putchar(ANS[ans[i]==1]);
flag=1;
return ;
}
if(!ans[now]){
ans[loop[bel[now]][0]]=1;
for(int i=1;i<len[bel[now]];i++)
ans[loop[bel[now]][i]]=-ans[loop[bel[now]][i-1]];
Dfs(now+1,sum+ans[now]);
if(flag) return;
for(int i=0;i<len[bel[now]];i++) ans[loop[bel[now]][i]]=-ans[loop[bel[now]][i]];
Dfs(now+1,sum+ans[now]);
if(flag) return;
for(int i=0;i<len[bel[now]];i++) ans[loop[bel[now]][i]]=0;
}
else Dfs(now+1,sum+ans[now]);
}
int main(){
int u;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&u);
Node(u,i),Node(i,u);
}
for(int i=1;i<=n;i++)
if(!vis[i])
ser(i);
Dfs(1,0);
}