2 – SAT 总结

来源:互联网 发布:淘宝禁止卖电子书了 编辑:程序博客网 时间:2024/06/04 23:55
 

2 – SAT 就是2判定性问题,是一种特殊的逻辑判定问题。

例,n对东西,每对只能选一个(i0或i1),不能不选。即:A or _A = 1 , A xor _A = 1

还存在一些约束关系(i0,j0),表示i0不能跟j0一起选。那需连边

i0-> j如果选i0的话必须选j1

j0-> i1如果选j0的话必须选i1

表示了一种递推的关系:选哪个必选哪一个

一般题目给的都是一对一对的东西,二选一,不能不选。本身那几对东西是没有关系的,然后题目会定义一些规则,或者我们自己加入条件(如二分的参数),使对与对之间有了一些约束关系: 1)选a必选b a->b

2)a必选 _a->a

对这个新图求SCC,同一SCC的要么全选,要么都不选。

如果发现a,_a在同一SCC,表明矛盾了。

不矛盾的话,对缩点后的图按照自底向上选择一个还未被标记的点,标记选上,然后把它的对立点及其前代都删除。

选择一个点要把它及其所有后代都选上。不选一个点,要把它及其前代都不选。

算法流程:

1、建图,求SCC。若某一个点,a与_a在同一个SCC,则无解,退出;

2、对求得的缩点,用原来的反图建一个DAG;

3、TopoSort得到一个原图的自底向上的序列,点是缩点后的SCC,没必要原来的每个点;

4、从左往右,对整个序列的点,如果未标记删除,就标记选它,同时把这个SCC里的所有原来的点的对立点及其各自的后代(因为是反图)都标记删除;

5、重复上面过程,最后被标记选上的SCC内的点都是选择的点。其个数=n

虽然被删除的点也是n个,但是他们可能会存在冲突关系。而标记选上的点间是相容的。

挺多用二分的。对于二分设定的参数,可能建图时要考虑这个条件,如POJ 2749

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

/*

    POJ 2749 Building roads

    题意:给出n个牛棚、两个特殊点S1,S2的坐标。S1、S2直连。牛棚只能连S1或S2

           还有,某些牛棚只能连在同一个S,某些牛棚不能连在同一个S

           求使最长的牛棚间距离最小 距离是曼哈顿距离

    使最大值最小,二分的经典应用

    二分枚举最大值limit,然后重新构图,用2-SAT判定可行性

    用布尔变量Xi表示第i个牛棚连到S1,~Xi表示连到S2

    检查每一个约束条件,构图:

    1.hate关系的i,j  Xi->~Xj  ~Xi->Xj  Xj->~Xi  ~Xj->Xi

    2.friend关系的i,j  Xi->Xj  ~Xi->~Xj  Xj->Xi  ~Xj->~Xi

    接下来的也要检查,因为引入参数,就是多了约束条件了

    这四种情况就是i,j到达对方的所有情况了

    3.dist(i,S1)+dist(S1,j)>limit  Xi->~Xj  Xj->Xi

    4.dist(i,S2)+dist(S2,j)>limit  ~Xi->Xj  ~Xj->Xi

    5.dist(i,S1)+dist(S1,S2)+dist(S2,j)>limit Xi->Xj  ~Xj->~Xi

    5.dist(i,S2)+dist(S2,S1)+dist(S1,j)>limit ~Xi->~Xj  Xj->Xi

    然后求SCC 判断Xi与~Xi是否在同一个SCC中,是的话就有矛盾

*/

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<vector>

using namespace std;

const int MAXN = 500;

const int INF = 1000000000;

int N,A,B;

struct Two

{

    int x,y;

};

Two pt[MAXN+10],s1,s2;

Two fre[MAXN*2+10],hate[MAXN*2+10];

struct Graph

{

    vector<int>G[2*MAXN+10],_G[2*MAXN+10];

    int ID[MAXN*2+10],ord[MAXN*2+10];

    int SCC;

    void init(int n)

    {

        for(int i=1;i<=n;i++)

        {

            G[i].clear();

            _G[i].clear();

        }

    }

    void add(int a,int b)

    {

        G[a].push_back(b);

        _G[b].push_back(a);

    }

    void dfs1(int u)

    {

        ID[u]=1;

        for(int i=0,size=_G[u].size();i<size;i++)

        {

            int v=_G[u][i];

            if(!ID[v])dfs1(v);

        }

        ord[++SCC]=u;

    }

    void dfs2(int u)

    {

        ID[u]=SCC;

        for(int i=0,size=G[u].size();i<size;i++)

        {

            int v=G[u][i];

            if(!ID[v])dfs2(v);

        }

    }

    void kosaraju()

    {

        memset(ID,0,sizeof(ID));

        SCC=0;

        for(int i=1;i<=2*N;i++)

        {

            if(!ID[i])dfs1(i);

        }

        memset(ID,0,sizeof(ID));

        SCC=0;

        for(int i=2*N;i;i--)

        {

            if(!ID[ord[i]])

            {

                SCC++;

                dfs2(ord[i]);//

            }

        }

    }

 

}graph;

inline int dist(Two &a,Two &b)

{

    return abs(a.x-b.x)+abs(a.y-b.y);

}

bool chk(int limit)

{

    graph.init(2*N);

    //build

    for(int i=1;i<N;i++)

        for(int j=i+1;j<=N;j++)

        {

            //dist(i,s1)+dist(j,s1)>L i0->j1 j0->i1

            if(dist(pt[i],s1)+dist(pt[j],s1)>limit)

            {

                graph.add(i,j+N);

                graph.add(j,i+N);

            }

            //dist(i,s2)+dist(j,s2)>L i1->j0 j1->i0

            if(dist(pt[i],s2)+dist(pt[j],s2)>limit)

            {

                graph.add(i+N,j);

                graph.add(j+N,i);

            }

            //dist(i,s1)+dist(s1,s2)+dist(s2,j)>L i0->j0 j1->i1

            if(dist(pt[i],s1)+dist(s1,s2)+dist(s2,pt[j])>limit)

            {

                graph.add(i,j);

                graph.add(j+N,i+N);

            }

            //dist(i,s2)+dist(s2,s1)+dist(s1,j)>L i1->j1 j0->i0

            if(dist(pt[i],s2)+dist(s2,s1)+dist(s1,pt[j])>limit)

            {

                graph.add(i+N,j+N);

                graph.add(j,i);

            }

        }

    for(int i=1;i<=A;i++)

    {

        //i0->j1,i1->j0,j0->i1,j1->i0

        graph.add(hate[i].x,hate[i].y+N);

        graph.add(hate[i].x+N,hate[i].y);

        graph.add(hate[i].y,hate[i].x+N);

        graph.add(hate[i].y+N,hate[i].x);

    }

    for(int i=1;i<=B;i++)

    {

        //i0->j0,i1->j1,j0->i0,j1->i1

        graph.add(fre[i].x,fre[i].y);

        graph.add(fre[i].x+N,fre[i].y+N);

        graph.add(fre[i].y,fre[i].x);

        graph.add(fre[i].y+N,fre[i].x+N);

    }

    graph.kosaraju();

    for(int i=1;i<=N;i++)

    {

        if(graph.ID[i]==graph.ID[i+N])return false;

    }

    return true;

}

int main()

{

    int Max;

    while(~scanf("%d%d%d",&N,&A,&B))

    {

        scanf("%d%d%d%d",&s1.x,&s1.y,&s2.x,&s2.y);

        //Max = 0;

        for(int i=1;i<=N;i++)

        {

            scanf("%d%d",&pt[i].x,&pt[i].y);

            //Max = max(Max,dist(pt[i],s1));

            //Max = max(Max,dist(pt[i],s2));

        }

        //Max = 2*Max+dist(s1,s2)+1;

        for(int i=1;i<=A;i++)

            scanf("%d%d",&hate[i].x,&hate[i].y);

        for(int i=1;i<=B;i++)

            scanf("%d%d",&fre[i].x,&fre[i].y);

 

        int left = 0,right = INF;

        while(right-left>1)

        {

            int mid = (right+left)>>1;

            if(chk(mid))right=mid;

            else left=mid;

        }

        if(right==INF)puts("-1");

        else printf("%d\n",right);

    }

    return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<pre class="brush:cpp">/*

POJ 2723 Get Luffy Out

    题意:给出N对钥匙,一对钥匙只能用一个 M个门,每个门上2个值,只要有其中一个值的钥匙即可开门了,问最大能开的门,门得按顺序开。

    既然是按顺序开,就可以二分枚举能开的门的个数

    然后用2-SAT判可行性

    建图方法为,对于门上的两个值a,b 显然有_a->b,_b->a

*/

 

/**//*

POJ 3678 Katu Puzzle

6个逻辑表达式,建图用

a  AND  b = 1      <==>  _a->a  _b->b

a  AND  b = 0       <==>  a->_b  b->_a

a  OR  b = 1        <==>  _a->b  _b->a

a  OR  b = 0        <==>  a->_a  b->_b

a  XOR b = 1        <==>  _a->b  a->_b  _b->a  b->_a

a  XOR b = 0        <==>  a->b  _a->_b  b->a  _b->_a

其实记住三个就够了,另外三个就两边取反就可以了

*/</pre>

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

<pre class="brush:cpp">/*

    POJ 3648 Wedding

    题意:n对夫妻,夫妻不能坐在同一排。m种通奸关系,有通奸关系的a,b不能同时坐在新娘的对面一排,求方案

    以新娘为参考系,x表示坐在新娘一侧,_x表示坐在对面一侧

    有通奸关系的a,b   有_a->b  _b->a

    最后!新娘一定要选上,所以连0h->0w 这样子连,拓扑排序后自底向上肯定会选上新娘

    缩点建图后再拓扑排序

    select的过程,遇到没有标记的块就选上它,并把它内所有点的对立点及其前代都标记删除

    最后删除掉的肯定是n个

    这里对反图建图,所以直接拓扑排序就可以自底向上了

*/

#include<cstdio>

#include<cstring>

#include<vector>

#include<queue>

using namespace std;

const int MAXN = 1000;

struct Graph

{

    vector<int>G[MAXN*2+10],_G[MAXN*2+10];

    vector<int>Dag[MAXN*2+10],inDag[MAXN*2+10];

    int n,SCC;

    int ID[MAXN*2+10],ord[MAXN*2+10];

    int in[MAXN*2+10],pos[MAXN*2+10];

    int color[MAXN*2+10];

    void init(int nn)

    {

        n=nn;

        for(int i=0;i<2*n;i++)

        {

            G[i].clear();

            _G[i].clear();

        }

    }

    void add(int u,int v)

    {

        G[u].push_back(v);

        _G[v].push_back(u);

    }

    void dfs1(int u)

    {

        ID[u]=1;

        for(int i=0,size=_G[u].size();i<size;i++)

        {

            int v=_G[u][i];

            if(ID[v]==-1)dfs1(v);

        }

        ord[SCC++]=u;

    }

    void dfs2(int u)

    {

        ID[u]=SCC;

        for(int i=0,size=G[u].size();i<size;i++)

        {

            int v=G[u][i];

            if(ID[v]==-1)dfs2(v);

        }

    }

    void kosaraju()

    {

        SCC = 0;

        memset(ID,-1,sizeof(ID));

        for(int i=0;i<2*n;i++)

            if(ID[i]==-1)dfs1(i);

        SCC = 0;

        memset(ID,-1,sizeof(ID));

        for(int i=2*n-1;i>=0;i--)

            if(ID[ord[i]]==-1)

            {

                dfs2(ord[i]);

                SCC++;

            }

    }

    bool chk()

    {

        for(int i=0;i<2*n;i+=2)

            if(ID[i]==ID[i+1])return false;

        return true;

    }

    void build_Dag()//对反图建DAG  这样topoSort就是自底向上的了

    {

        for(int i=0;i<SCC;i++)

        {

            Dag[i].clear();

            inDag[i].clear();

        }

        memset(in,0,sizeof(in));

        for(int u=0,v;u<2*n;u++)

        {

            inDag[ID[u]].push_back(u);

            for(int i=0,size=_G[u].size();i<size;i++)

            {

                v=_G[u][i];

                if(ID[u]!=ID[v])

                {

                    Dag[ID[u]].push_back(ID[v]);

                    ++in[ID[v]];

                }

            }

        }

    }

    void topo_Sort()

    {

        queue<int>Q;

        for(int i=0;i<SCC;i++)

            if(in[i]==0)Q.push(i);

        int cnt = 0;

        while(!Q.empty())

        {

            int u=Q.front();Q.pop();

            pos[cnt++]=u;

            for(int i=0,size=Dag[u].size();i<size;i++)

            {

                int v=Dag[u][i];

                if(--in[v]==0)Q.push(v);

            }

        }

    }

    void setColor(int u)

    {

        color[u]=2;

        for(int i=0,size=Dag[u].size();i<size;i++)

        {

            int v=Dag[u][i];

            if(color[v]!=2)setColor(v);

        }

    }

    void select()

    {

        memset(color,0,sizeof(color));

        for(int i=0;i<SCC;i++)

        {

            int u=pos[i];

            if(color[u]==0)

            {

                color[u]=1;//select

                for(int j=0,size=inDag[u].size();j<size;j++)//set every _v

                {

                    int v=inDag[u][j]^1;

                    if(color[ID[v]]==2)continue;

                    setColor(ID[v]);

                }

            }

        }

    }

    void solve()

    {

        build_Dag();

        topo_Sort();

        select();

        for(int i=2;i<2*n;i++)

            if(color[ID[i]]==1)printf("%d%c ",i/2,(i&1)?'h':'w');

        puts("");

    }

 

}graph;

 

int main()

{

    //freopen("in","r",stdin);

    int n,m,a,b;

    char ca,cb;

    while(scanf("%d%d",&n,&m),n)

    {

        graph.init(n);

        for(int i=0;i<m;i++)

        {

            scanf("%d%c%d%c",&a,&ca,&b,&cb);

            a=a*2+(ca=='h');

            b=b*2+(cb=='h');

            //_a->b _b->a

            graph.add(a^1,b);

            graph.add(b^1,a);

        }

        //0h->0w  0wife一定要选

        graph.add(1,0);

        graph.kosaraju();

        if(!graph.chk())puts("bad luck");

        else graph.solve();

    }

    return 0;

}</pre>

还有2道

POJ 3207 把线当成SAT的点

POJ 3683 跟wedding类似