NEERC 2016年B题 Binary code

读研以后好久没有研究算法竞赛的题目了。这段时间算法课,刚好作业是要讲算法,就选了一道题目研究了一下,感觉还是蛮有意思的。但是说实话本弱鸡太菜了,靠自己肯定解决不了这么难的题,就参考了别人的代码啦(这个也找了半天,有好多人的实现都没看懂)。而且这个过程也接触到了以前没有接触过的构图技巧,叫什么前后缀优化建图,还是很有意义的。

题目链接(里面B题):

https://codeforces.com/gym/101190

题意:

给定n个0/1串,每个串至多有一个问号。问你是否能够找到方案,使得这些问号用0/1填充后,各个串之间互不为前缀。如果是,输出YES,并输出方案。否则,输出NO。

代码主要参考:

https://blog.csdn.net/kzn2683331518/article/details/101431239

主要思路:

互不为前缀,出现前缀这种字眼,可以联想到字典树。

对于问号的处理,用0替代插入字典树,用1替代插入至字典树中。

然后呢,替代字符串要么取0要么取1,对应于命题True/False,同时还有限制条件,即一个树的分支上最多只能有一个替代字符串,对分支上任意两个串a、串b,有条件(not a) or (not b),这样看就转换成了2-sat问题。

关于字典树,2-sat读者可以知乎百度上搜搜了解下啦,或者也可以参考算法竞赛入门经典训练指南

(要是参加比赛的话私以为2-sat可能出现比较少,但里面用到的tarjan算法求有向图强联通分量这个更重要更普遍点,读者最好理解这个算法)

然而事情还没有结束。考虑极端情形,如果n个串在一个分支上,这样加限制条件的话,限制条件数就有n的平方级别个,太多了,这个时候就有一种叫前后缀优化的东西~~~(点到为止,主要看代码注释)

下面就是自己参考了别人代码,写了一遍,加了一点注释的结果:

#include
using namespace std;
const int maxn=4e6+10;
vector G[maxn];
//loc[node]存储替代问号以后,字符串终结点在node上的字符串序号 
vector loc[maxn];
int sumn,n;
int no[maxn],lowlink[maxn],sccno[maxn],dfs_block,scc_cnt;
int wh[maxn],pos[maxn];
int ch[maxn][2],father[maxn],tot;
string str[maxn];
stack S;
//加边u->v,即若u则必须v 
void add_clause(int u,int v){
	G[u].push_back(v);
}
void link(int u,int v){
	add_clause(u,v);
	add_clause(v^1,u^1);  //逆否命题 
}
//tarjan算法中深度优先遍历部分 
void dfs(int u) {
	no[u]=lowlink[u]=++dfs_block;
	S.push(u);
	for (int i=0;i>n;
	for (int i=1;i<=n;i++){
		cin>>str[i];
		int whpos=-1;
		int sz=str[i].size();
		for (int j=0;j

 

你可能感兴趣的:(解题报告)