2016-2017 ACM-ICPC, Central Europe Regional Contest (CERC 16) L - Lost Logic

2016-2017 ACM-ICPC, Central Europe Regional Contest (CERC 16) L - Lost Logic

八月 06, 2021

题目描述

有$n$个布尔变量和三个可行解,现在要求构造不超过$500$条限制条件,使得这些可行解成为在限制条件下仅有的三个可行解。

分析与解

这个题很有意思。

首先这个题有一定的伪装性,让人以为往$2-SAT$​​​上去想,其实跟2-SAT没有半毛钱关系。主要在于怎么给变量分类,化繁为简,同时用数理逻辑知识把约束关系表达出来。

因为只有3个可行解,所以所有变量在三个解中的取值无非最多8种。我们把变量分成以下几类:

①全0或全1,那么相当于这个变量的值就是确定的。所以赋值

②两个变量取值恰好相同,那么$x_1$确定了,$x_2$就确定了,所以赋值

③两个变量恰好取值相反

分析完以上三类情况,我们发现自由变量数目不会超过3个。原因就是一共的8种情况,刨去全0和全1的,剩下6种是对称的。(比如001和110,010和101)

如果只有两个自由变量,那么本来应该可以由4组解,现在给出了3组,所以只需要讨论一下限制死第四组就行。

如果有三个自由变量,那么8种情况剩下5组无论如何也限制不住,就是本题的无解,输出-1.

代码

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
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100
struct dat {
int x;
bool val_x;
int y;
bool val_y;
};
int n;
int constr[3][MAXN];
bool vis1[MAXN],vis2[MAXN];
vector<dat> ans;
vector<int> fre;
bool check(int x,int y,bool val_x,bool val_y) {
if(constr[0][x]==val_x && constr[0][y]==val_y)return false;
if(constr[1][x]==val_x && constr[1][y]==val_y)return false;
if(constr[2][x]==val_x && constr[2][y]==val_y)return false;
return true;
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++)scanf("%d",&constr[0][i]);
for(int i=1; i<=n; i++)scanf("%d",&constr[1][i]);
for(int i=1; i<=n; i++)scanf("%d",&constr[2][i]);
for(int i=1; i<=n; i++) {
if(constr[0][i]==constr[1][i] && constr[1][i]==constr[2][i]) {
vis1[i]=true;
if(constr[0][i]==1)
ans.push_back({i,0,i,1});
else ans.push_back({i,1,i,0});
}
}
for(int i=1; i<=n; i++) {
if(vis1[i])continue;
if(!vis2[i])fre.push_back(i);
for(int j=i+1; j<=n; j++) {
if(vis1[j])continue;
if(vis2[i] && vis2[j])continue;
if(constr[0][i]==constr[0][j] && constr[1][i]==constr[1][j] && constr[2][i]==constr[2][j]) {
vis2[i]=true,vis2[j]=true;
ans.push_back({i,1,j,1});
ans.push_back({j,1,i,1});
} else if(constr[0][i]!=constr[0][j] && constr[1][i]!=constr[1][j] && constr[2][i]!=constr[2][j]) {
vis2[i]=true,vis2[j]=true;
ans.push_back({i,1,j,0});
ans.push_back({j,0,i,1});
}

}
}
int k1,k2;
if(fre.size()>=3) {
printf("-1\n");
return 0;
} else if(fre.size()==2) {
k1=fre[0],k2=fre[1];
if(check(k1,k2,0,0))ans.push_back({k1,0,k2,1});
else if(check(k1,k2,0,1))ans.push_back({k1,0,k2,0});
else if(check(k1,k2,1,0))ans.push_back({k1,1,k2,1});
else if(check(k1,k2,1,1))ans.push_back({k1,1,k2,0});
}
printf("%d\n",ans.size());
for(auto t : ans) {
if(t.val_x==0)printf("!");
printf("x%d -> ",t.x);
if(t.val_y==0)printf("!");
printf("x%d\n",t.y);
}
return 0;
}