描述
You are given a logic expression tree of N nodes which are numbers from 1 to N. The leaf nodes are boolean values(TRUE, FALSE) and the non-leaf nodes are logic operators(AND, OR). The value of tree is the boolean value when you calculate the expression from leaves to the root. For example below is a logic expresssion tree whose valus is TRUE AND (FALSE OR TRUE) = TRUE.
AND
/ \
T OR
/ \
F T
Now you want to reverse the value of the tree (from TRUE to FALSE or vice versa). You want to know how many operators at least are needed to be reversed (from AND to OR or vice versa). For example you only need to reverse the OR operator into AND to make the tree from TRUE to FLASE.
输入
The first line contains an integer N. (1 <= N <= 100)
The i-th line of the following N lines contains an integer Pi and a string Si.
Pi denotes the number of the i-th node’s parent node. Pi = 0 indicates that the i-th node is the root. (0 <= Pi <= N)
Si is either TRUE, FALSE, AND or OR denoting the boolean value or logic operator of the node.
输出
The minimum number of operators needed to be reversed. If you cannot reverse the value no matter how many operators are reversed output -1.
样例输入
5
0 AND
1 TRUE
1 OR
3 FALSE
3 TRUE
样例输出
1
题意:
给了一颗二叉树,其中非叶子节点是逻辑运算符,叶子节点的是bool值。问要把整棵树的值变为相反的最少需要改变多少个运算符。
思路:
最近比较忙,没有认真更题,接下来好好更!!!!
借鉴一下讨论区的思路
本题是一道树形DP的题目。
我们不妨用v[x]表示以节点x为根的子树的值,f[x]表示为了翻转v[x],需要至少改变多少个运算符。显然f[1]就是最后的答案。
而影响v[x]值的因素只有3个:
1) x节点的运算符。改变x节点的运算符会使f[x]增加1。
2) x左子树的值,v[x.leftchild]。改变v[x.leftchild]会使f[x]增加f[x.leftchild]。
3) x右子树的值,v[x.righthild]。改变v[x.rightchild]会使f[x]增加f[x.rightchild]。
以上3种因素都可能改变/不改变二选一,所以一共有8种组合。我们只需枚举这8中组合,找到哪些组合会使v[x]改变,并从中找到f[x]最小的组合即可。
于是我们得到了一个递归或者说自底向上DP的算法。时间复杂度是O(N)的。
说一下如何递推,需要判断当前节点的逻辑运算符,比如,如果当前的逻辑运算符是AND且以当前节点为根的树的值是True的话,那么把当前这棵树的值变为False只需要改变其左子树或者右子树的值为False那么整个树的值就变为False了,具体的取两者之间操作次数小的。如果当前节点的逻辑运算符是AND,且以当前节点为根的树的值是False,那么逆转这个树的值,有两种操作:
第一种:不改变当前节点的操作符,让它的左右子树都变为True 总的操作数为
tmp=solve(a[u].l,1)+solve(a[u].r,1)
t
m
p
=
s
o
l
v
e
(
a
[
u
]
.
l
,
1
)
+
s
o
l
v
e
(
a
[
u
]
.
r
,
1
)
,
第二种:变当前的操作符为OR,那么只需要左右子树其中的一个值变为True就可以满足条件。总的操作数为
tmp=1+min(solve(a[u].l,1),solve(a[u].r,1))
t
m
p
=
1
+
m
i
n
(
s
o
l
v
e
(
a
[
u
]
.
l
,
1
)
,
s
o
l
v
e
(
a
[
u
]
.
r
,
1
)
)
直觉上觉得第二种情况一定比第一种情况更优,所有没加第一种情况也ac了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 1e3+7;
const int inf = 1e9+7;
struct node
{
int l,r;
string s;
bool ans;
};
node a[maxn];
int sum[maxn];
void dfs(int u)
{
if (a[u].l == 0) {
if (a[u].s[0] == 'T') {
a[u].ans = 1;
}
else
a[u].ans = 0;
}
else {
dfs(a[u].l);
dfs(a[u].r);
if (a[u].s[0] == 'A') {
a[u].ans = a[a[u].l].ans && a[a[u].r].ans;
}
else {
a[u].ans = a[a[u].l].ans || a[a[u].r].ans;
}
}
}
int solve(int u, bool flag)
{
if (a[u].ans == flag) {
return 0;
}
if (a[u].l == 0) {
if (a[u].ans == flag)
return 0;
else
return inf;
}
if (sum[u] != -1)
return sum[u];
int ans = inf;
if (a[u].s[0] == 'A') {
if (a[u].ans) { //u节点的操作符是AND 且以u为根的子树的值为True
int tmp = min(solve(a[u].l,0),solve(a[u].r,0)); //只需要改变左子树or右子树的值为False,取操作最小的那个
ans = min(ans,tmp);
}
else { //改变当前节点操作符为OR(所以+1),那么左右子树至少一个为True,取最小的那个。
int tmp = 1 + min(solve(a[u].l,1),solve(a[u].r,1));
ans = min(ans,tmp);
}
}
else {
if (a[u].ans) { //u节点的操作符是OR,且以u为根的子树的值为True
int tmp = 1 + min(solve(a[u].l,0),solve(a[u].r,0));
ans = min(ans,tmp);
}
else {
int tmp = min(solve(a[u].l,1),solve(a[u].r,1));
ans = min(ans,tmp);
}
}
return sum[u] = ans; //return的还是ans,且把ans的值赋给sum[u]
}
int main()
{
int n;
cin >> n;
int x,root;
string op;
for (int i = 1; i <= n; ++i) {
a[i].l = a[i].r = 0;
sum[i] = -1;
}
for (int i = 1; i <= n; ++i) {
cin >> x >> a[i].s;
if (x) {
if (a[x].l == 0) {
a[x].l = i;
}
else
a[x].r = i;
}
else {
root = i;
}
}
dfs(root);
int ans = solve(root,!a[root].ans);
if (ans >= inf)
cout << "-1"<< endl;
else
cout << ans << endl;
return 0;
}