poj.org/problem?id=3383
这题不难,但是AC比例也很低
| Total Submissions: 101 | Accepted: 14 | Special Judge |
大致题意是:
1. 定义4个集合操作,有不同的优先级,给定一个方程,和已知的一些变量的值 (每个变量都是一个集合),包括全集,判断方程中的未知变量是否有解
思路
集合变量是否有解比较简单,只要判断对于全集中的任一个元素,如何这个方程左边和右边的计算结果可以同时包含这个元素,或是同时不包含这个元素,那么这个元素就可以满足方程;如果某个元素的取舍无法满足方程,则说明方程无界;
对于未知变量的解也很简单,对已任意元素,如果不包含这个元素就可以满足方程,那么就可以按照左右两边的计算结果都不包含该元素来贪心赋值即可
所以问题就归结为
1. 解析方程字符串为二元运算表达式数
2. 对于任意元素, 依次dfs检查以当前节点为根的表达式数是否可以包含/不包含这个元素,如果某个节点既不能包含也不能不包含某个元素,这该表达式树无效
3. 对于有效的表达式树,再次dfs设置那些未知变量是否包含这个元素
实现
代码量比较大,感觉这题主要是代码能力,如何解析表达式应该算是基本功吧 (去年写了一个基于公式的k8s HPA扩展controller,写起来还是比较轻松的)
对于又优先级的表达式,只需要维护一个优先级递增的栈即可, 这里只有四种操作,所以栈的最大长度是4
int parse(char *p, int *pi) {
int i =*pi, j, k;
char op[8], oc=0, co, cr, oo, or, bbb;
int nn[8], nc=0, n1, n2, n;
while(1) {
while(p[i]&&(p[i]==' '||p[i]=='\t')) i++; if (p[i]==0) break;
if (p[i]==')') break;
if (p[i]=='(') {
i++; n=parse(p, &i); if (p[i]!=')') assert(0); i++;
nn[nc++]=n;
} else if (p[i]=='+'||p[i]=='-'||p[i]=='*'||p[i]=='^') {
co=p[i]; cr=rank[co];
while(oc) {
oo=op[oc-1]; or=rank[oo]; if (or<cr) break;
n1=nn[--nc]; n2=nn[--nc];
n=alloc(oo, n2, n1); nn[nc++]=n;
oc--;
}
op[oc++]=co;
i++; continue;
} else {
// var
j=i; while(vmk[p[j]]) j++;
bbb=p[j]; p[j]=0;
k = addn(p+i); p[j]=bbb;
n=alloc(-1, k, -1);
nn[nc++]=n;
i=j;
}
}
while(oc) {
oc--; oo=op[oc];
n1=nn[--nc]; n2=nn[--nc];
n=alloc(oo, n2, n1); nn[nc++]=n;
}
*pi=i;
if (nc==0) return -1;
return nn[0];
}
检查表达式树是否可以包含/不包含某个元素, 这里记录了计算结果,因为在最后赋值的时候还会用到
char check(int r, int k) {
int i;
char ml, mr, mm;
if (obs[r]==-1) {
i = lhs[r];
if (nmk[i]) {
if (smk[i][k/32]&(1<<(k%32))) dpm[r]=2;
else dpm[r]=1;
} else dpm[r]=3;
} else {
ml=check(lhs[r], k);
mr=check(rhs[r], k);
mm=0;
if (obs[r]=='+') {
if (ml&mr&1) mm|=1;
if (((ml&2)&&mr) || ((mr&2)&&ml)) mm|=2;
} else if (obs[r]=='*') {
if (((ml&1)&&mr) || ((mr&1)&&ml)) mm|=1;
if ((ml&mr)&2) mm|=2;
} else if (obs[r]=='-') {
if (((ml&1)&&mr)||((ml&2)&&(mr&2))) mm|=1;
if ((ml&2)&&(mr&1)) mm|=2;
} else if (obs[r]=='^') {
if (((ml&1)&&(mr&1)) || ((ml&2)&&(mr&2))) mm|=1;
if (((ml&1)&&(mr&2)) || ((ml&2)&&(mr&1))) mm|=2;
} else assert(0);
dpm[r]=mm;
}
return dpm[r]
}
赋值时,一旦确认方程是有效的,那就直接找可行路径即可
void setv(int r, int m, int k) {
int i;
char ml, mr, mm;
if (obs[r]==-1) {
i = lhs[r];
if (nmk[i]==0&&m) smk[i][k/32]|=(1<<(k%32));
} else {
ml=dpm[lhs[r]];
mr=dpm[rhs[r]];
if (obs[r]=='+') {
if (m==0) {
setv(lhs[r], 0, k);
setv(rhs[r], 0, k);
} else {
if ((ml&2)&&mr) {
setv(lhs[r], 1, k);
if (mr&1) setv(rhs[r], 0, k);
else setv(rhs[r], 1, k);
} else if ((mr&2)&&ml) {
setv(rhs[r], 1, k);
if (ml&1) setv(lhs[r], 0, k);
else setv(lhs[r], 1, k);
}
}
} else if (obs[r]=='*') {
if (m==0) {
if ((ml&1)&&mr) {
setv(lhs[r], 0, k);
if (mr&1) setv(rhs[r], 0, k);
else setv(rhs[r], 1, k);
} else {
setv(rhs[r], 0, k);
if (ml&1) setv(lhs[r], 0, k);
else setv(lhs[r], 1, k);
}
} else {
setv(lhs[r], 1, k);
setv(rhs[r], 1, k);
}
} else if (obs[r]=='-') {
if (m==0) {
if ((ml&1)&&mr) {
setv(lhs[r], 0, k);
if (mr&1) setv(rhs[r], 0, k);
else setv(rhs[r], 1, k);
} else {
setv(lhs[r], 1, k);
setv(rhs[r], 1, k);
}
} else {
setv(lhs[r], 1, k);
setv(rhs[r], 0, k);
}
} else if (obs[r]=='^') {
if (m==0) {
if ((ml&1)&&(mr&1)) {
setv(lhs[r], 0, k);
setv(rhs[r], 0, k);
} else {
setv(lhs[r], 1, k);
setv(rhs[r], 1, k);
}
} else {
if ((ml&1)&&(mr&2)) {
setv(lhs[r], 0, k);
setv(rhs[r], 1, k);
} else {
setv(lhs[r], 1, k);
setv(rhs[r], 0, k);
}
}
} else assert(0);
}
}
这题的思路是比较简单的,只是代码量有点大,刚开始写这题的时候我是满心抗拒的,折腾的2天才慢悠悠一点点写完.....基本上写完就1A了, 算是1A,因为有个变量名or在c++/g++里编译不过,compiler error了一次,目前排名第一
| Rank | Run ID | User | Memory | Time | Language | Code Length | Submit Time |
|---|---|---|---|---|---|---|---|
| 1 | 23272023 | lddlinan | 424K | 110MS | GCC | 7036B | 2022-02-25 19:52:10 |
| 2 | 23112867 | nwin | 1200K | 125MS | G++ | 2819B | 2021-11-23 00:05:11 |
| 3 | 12958087 | vjudge2 | 5648K | 172MS | C++ | 8551B | 2014-06-08 15:33:20 |
一道关于集合操作的POJ题目,要求根据给定的集合运算判断方程是否有解。通过解析表达式树,使用深度优先搜索检查每个元素的取舍是否满足方程。若所有未知变量都有解,则方程有解。主要难点在于解析表达式和构造有效的表达式树。

1247

被折叠的 条评论
为什么被折叠?



