bzoj1997
bzoj2199
bzoj1823
bzoj4078
poj2723√
题意:
有2*n把钥匙分成n组,每组两个钥匙如果选择了一把另一把就会消失。有m个门,每个门上有两把锁,只要打开一把锁就能打开门,要打开第i+1个门必须先打开第i个门。
求最多能打开多少门。
分析:
每把钥匙有两个状态(用或不用),把这作为2-sat的两个选项
然后是加条件:
a、b绑在一起,则选a就不选b,选b就不选a,建边 a−>!b,b−>!a
c、d在同一个门上,则不开c就开d,不开d就开c,建边 !c−>d,!d−>c
然后二分答案都可以了
poj2749
题意:
好像是平面上有两个点 s1 和 s2 ,这两个点是连在一起的,然后给了n个点,然后这些点要么连 s1 要么连 s2 ,要求任意两点的最大距离最小,然后有些点不能连在同一边,也就是不能同时连 s1 或者 s2 ,有些点必须连在同一边,就是同时连 s1 或者同时连 s2 。这题要求的是曼哈顿距离。两个点a和b的距离有4种, a−>s1−>b ; a−>s2−>b ; a−>s1−>s2−>b ; a−>s2−>s1−>b 。
求最长的点之间的距离最短是多少。
poj3207
题意:
一个圆周上有n个点标号依次从0到n-1,然后有m条线段每个线段以其中两个点为端点,线可以在圆正面或者背面。
问是否存在一种方式使得任意两线段都不相交,端点相交不算。
poj3648√
题意:
一堆夫妇去参加一对新人的婚礼。人们坐在一个很长很长的桌子的两侧(面对面)。新郎新娘在桌子头面对面座。
新娘不希望看见她对面的一排有一对夫妇坐着(夫妇需要分开两排座)。
同时,一些人之间有暧昧关系(同性异性均有可能),新娘也不希望有暧昧关系的人同时坐在她对面的一排。
问你可否满足新娘的要求,可以的话,输出一种方案。
分析:
根据题意建图。都是 ax or ay (一个要输出方案的模板)
poj3678
题意:
构造一个长度为N的01数列,使得第i位和第j位通过特定的逻辑运算结果为k。
poj3683
题意:
有n个婚礼,每个婚礼有起始时间si,结束时间ti,还有一个主持时间ti,ti必须安排在婚礼的开始或者结束,主持由祭祀来做,但是只有一个祭祀,所以各个婚礼的主持时间不能重复,问你有没有可能正常的安排主持时间,不能输出no,能的话要输出具体的答案:即每个婚礼的主持时间段是什么样的。
poj2296
题意:
坐标轴上有N个点,要在每个点上贴一个正方形,这个正方形的横竖边分别和x,y轴平行,并且要使得点要么在正方形的上面那条边的中点,或者在下面那条边的中点,并且任意两个点的正方形都不重叠(可以重边)。问正方形最大边长可以多少?
参考资料:
http://www.cppblog.com/MatoNo1/archive/2011/07/13/150766.html
http://blog.csdn.net/lethic/article/details/7803841
http://blog.csdn.net/d891320478/article/details/8287435
现有一个由N个布尔值组成的序列A,给出一些限制关系,比如 ax and ay=0、ax or ay or az=1 等,要确定 a0..an 的值,使得其满足所有限制关系。这个称为SAT问题.
特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题。
由于在2-SAT问题中,最多只对两个元素进行限制,所以可能的限制关系共有11种:
ax
!ax
ax and ay
ax and !ay
ax or ay
ax or !ay
ax xor ay
ax xor !ay
!(ax and ay)
!(ax or ay)
!(ax xor ay
发现: !(ax or ay)=!ax and !ay
ax and ay和ax and !ay和!ax and !ay 都是 ax 和 ay 之间的关系。所以限制关系只有9种
在实际问题中,2-SAT问题在大多数时候表现成以下形式:有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。
其实2-SAT问题的建模是和实际问题非常相似的。
建立一个2N阶的有向图,其中的点分为N对,每对点表示布尔序列A的一个元素的0、1取值(以下将代表A[i]的0取值的点称为i,代表A[i]的1取值的点称为i’)。显然每对点必须且只能选取一个。然后,图中的边具有特定含义。若图中存在边 <i,j> ,则表示若选了i必须选j。
可以发现,上面的9种限制关系中,后7种二元限制关系都可以用连边实现,比如:
!(ax and ay) 需要连两条边 <x,y′> 和 <y,x′> ,
ax or ay 需要连两条边 <x′,y> 和 <y′,x> 。
而前两种一元关系:
ax (即x必选),可以通过连边 <x′,x> 来实现,
!ax (即x不能选),可以通过连边 <x,x′> 来实现。
因为连边关系->具有传递性,故如果存在一个强连通分量,则选取其中任意一个结点,其余的结点也同样会被选到,故可以先缩点,如果x和~x在同一个强连通分量里,则无解。
否则,得到新的有向无环图。
在新图中按照拓扑反序开始选择,若当前结点没有选择,则选择当前结点并删除对称结点以及对称结点的前驱。
因为采用了拓扑反序,不存在未处理的后继
//模板poj3648
#include
#include
#include
using namespace std;
const int N=100100;
int dfn[N],low[N],scc[N];
int q[N],v[N],top,tm,cnt;
int h[N],ans[N],col[N];
int deg[N],c[N];
int n,m,tot,x,y;
struct edge{int y,next;}g[N];
struct bian{int x,y;}b[N];
//-------------------原图增加一条边
void adp(int x,int y){
g[++tot].y=y;
b[tot].x=x;
b[tot].y=y;
g[tot].next=h[x];
h[x]=tot;
}
//-------------------缩点图增加一条边
void ads(int x,int y){
g[++tot].y=y;
g[tot].next=h[x];
h[x]=tot;
}
//-------------------Tarjan算法求强连通分量
void tarjan(int x){
dfn[x]=low[x]=++tm;
q[++top]=x;v[x]=1;
for (int i=h[x];i;i=g[i].next)
if (!dfn[g[i].y]){
tarjan(g[i].y);
low[x]=min(low[x],low[g[i].y]);
} else
if (v[g[i].y]) low[x]=min(low[x],dfn[g[i].y]);
if (low[x]==dfn[x]){
cnt++;
while (q[top]!=x){
scc[q[top]]=cnt;
v[q[top--]]=0;
}
scc[q[top]]=cnt;
v[q[top--]]=0;
}
}
//---------------------拓扑排序求解
int check(){
for (int i=0;i<2*n;i++)
if (!dfn[i]) tarjan(i);
for (int i=0;iif (scc[i]==scc[i+n])
return 0; else
{
c[scc[i]]=scc[i+n];
c[scc[i+n]]=scc[i];//在缩点的图中标记互斥的缩点。(原来互斥,现在也互斥)
}
memset(h,0,sizeof(h));
memset(col,0,sizeof(col));
int tnt=tot;tot=0;
for (int i=1;i<=tnt;i++)
if (scc[b[i].x]!=scc[b[i].y]){
ads(scc[b[i].y],scc[b[i].x]);//反向,是因为u--->v,如果选择了u,必须选择v,则应该反向,求入度为0的缩点
deg[scc[b[i].x]]++;
}
//-----------------开始拓扑排序
int l=0,r=0;
for (int i=1;i<=cnt;i++)
if (!deg[i])
q[++r]=i;
while (lint x=q[++l];
if (!col[x]){col[x]=1;col[c[x]]=-1;}//对于未着色的点x,将x染成红色1,同时将与x互斥的点c[x]染成蓝色-1。
for (int i=h[x];i;i=g[i].next){
if (--deg[g[i].y]==0)
q[++r]=g[i].y;
}
}
for (int i=0;iif (col[scc[i]]==1)
ans[i]=1;
return 1;
}
int main(){
while (scanf("%d%d",&n,&m) && n && m){
memset(h,0,sizeof(h));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(v,0,sizeof(v));
memset(ans,0,sizeof(ans));
memset(deg,0,sizeof(deg));
tot=cnt=top=tm=0;
for (int i=1;i<=m;i++){
char s1,s2;
scanf("%d%c %d%c",&x,&s1,&y,&s2);
if (s1=='h' && s2=='h')
adp(y+n,x),adp(x+n,y);
if (s1=='h' && s2=='w')
adp(x+n,y+n),adp(y,x);
if (s1=='w' && s2=='h')
adp(x,y),adp(y+n,x+n);
if (s1=='w' && s2=='w')
adp(x,y+n),adp(y,x+n);
}
adp(0,n);
if (check()){
for (int i=1;iif (ans[i])
printf("%dh ",i); else
printf("%dw ",i);
} else printf("bad luck");
printf("\n");
}
}