Dispwnl
2018-08-14 08:33:15
竟然没有
2-SAT 的百度词条 _(:з」∠)
这样两种状态之间,
如图,如果按照刚才的方法连边,应该是
等等
就是你给我搞一个试试,所以
发现点与点之间是单向边,那么我们连着连着就可能出现强连通分量
有什么影响吗?
这样构成的一张图,可以看出
这样选
所以我们可以通过找强连通分量来判断是否存在可行方案
复杂度:
因为
注意这里是选
所以对于每个点的两种状态,可以选拓扑序大的,舍弃掉另一个(选一个就不能选另一个了)
每个点都判断与对立点谁的拓扑序大
注意这里可以不用再搞一遍拓扑排序,因为
编号和拓扑序是相反的,即要每两对点找编号小的
输出每个点状态就行了
两个点的关系还有很多,列举几个(这里存在和不存在是一个点的两种状态):
上面的输出方案是随意输出一种即可,但是要是要求输出字典序最小的答案呢?
也是很好解决的,
点从
如果某个点发生冲突了(对立点被选了),这个点就不能选,与ta相连的点不能选,与ta相连的点的相连点不能选,与ta...
这样最后被选中的点就是这字典序最小的解了,复杂度:
发现我大部分在水字数啊emmmm
真·模板题
将3个状态转成2个状态
处理必须选的、必须不选的、可选可不选的
似乎字数还不够我再水水选一道题讲讲qwq
上面的第三题,是按
但是通过拓扑序很难判断三种状态
考虑
这样
如果两个状态都能被选,说明这个点可选可不选;如果两个状态都不能选,说明没法构造出可行的方案,输出
void dfs(int x)//use表示是否选这个点
{
use[x]=1;
for(int i=h[x];i;i=c[i].x)
if(!use[c[i].y]) dfs(c[i].y);
}
bool look(int x)
{
memset(use,0,sizeof(use));
dfs(x);//选择这个点和ta后面的点
for(int i=1;i<=n;++i)
if(use[i]&&use[i+n]) return 0;//判断方法还是一样的,如果两个状态都被选了说明不可行
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
//构造图过程略去
for(int i=1;i<=n;++i)
{
bool d1=look(i),d2=look(i+n);//判断两个状态
if(!d1&&!d2) return printf("IMPOSSIBLE"),0;
if(d1&&d2) ans[i]='?';
if(d1&&!d2) ans[i]='N';
if(!d1&&d2) ans[i]='Y';//记录每种情况
}
for(int i=1;i<=n;++i)
printf("%c",ans[i]);
return 0;
}
再看一下第二道题水到200行
很显然
这个是按
但是
发现
所以考虑枚举
这样复杂度是
发现
所以只用枚举前两个就行了,复杂度
bool look()//枚举x状态进行判断是否可行
{
memset(h,0,sizeof(h));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
num=0,Col=0;
for(int i=1;i<=m;++i)
{
if(S[s[i].i]==s[i].hi+32) continue;//S是场地字符串,s是给定4元组
int tt=s[i].hi+32==pos[0][s[i].i]?s[i].i:s[i].i+n;//pos为每个点代表的两个状态
if(S[s[i].j]==s[i].hj+32) add(tt,re[tt]);//re为对立点
else
{
int ttt=s[i].hj+32==pos[0][s[i].j]?s[i].j:s[i].j+n;
add(tt,ttt),add(re[ttt],re[tt]);
}
}
for(int i=1;i<=2*n;++i)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;++i)
if(col[i]==col[i+n]) return 0;
return print(),1;
}
注意:中间那里是判断
如果能被选,就按
这下字数应该水够了qwq