hiho217 Logic Expression Tree

hiho217 Logic Expression Tree

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

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

题意分析:

1.题是什么?

    就是给你一个逻辑表达树,叶节点为真或假,非叶节点为”AND”或”OR”,其实就是与和或,每个结点的值为此节点的所有子节点的AND值或者OR值,整个树的真值理解为根节点的值.现在你可以把任意的AND改为OR,OR改为AND,翻转一次记作一次操作,问你最少需要几次翻转可令整个树的真值改变.

2.思路分析

    我们要求的是最少要几次操作,最少自然马上想到贪心,我们如果从树根开始尝试翻转,找尽可能靠近根节点的翻转方法,主观思维上想这样的方法可能就是最少的翻转了,因为越远离树根涉及的需要翻转的逻辑点就越多,然而只是可能,我就这个方法写了一个解答,交上去幸运的ac了,这个方法需要预处理出每个结点在子树不翻转情况下的值,并且我无法理论证明它的正确性.不过还是放在下面,后面有更正确的方法.

#include <iostream>
#include <vector>
using namespace std;
const int inf=(1<<30);

int n;
vector<pair<string,int> > a[101];

bool nodevalue[101]; //每个节点的值
bool prepare(int t1,int t2){ //递归预处理值
	bool res;
	string temp=a[t1][t2].first;
	int number=a[t1][t2].second;
	if(temp=="AND"){
		res=true;
		int num=a[number].size();
		for(int i=0;i<num;i++){
			string ts=a[number][i].first;
			int tn=a[number][i].second;
			if(ts=="TRUE") nodevalue[tn]=true;
			else if(ts=="FALSE"){
				nodevalue[tn]=false;
				res=false; 
			}
			else{
				bool tp=prepare(number,i);
				nodevalue[tn]=tp;
				res=res&&tp;
			}
		}
	}
	else if(temp=="OR"){
		res=false;
		int num=a[number].size();
		for(int i=0;i<num;i++){
			string ts=a[number][i].first;
			int tn=a[number][i].second;
			if(ts=="TRUE"){
				nodevalue[tn]=true;
				res=true;
			}
			else if(ts=="FALSE") nodevalue[tn]=false; 
			else{
				bool tp=prepare(number,i);
				nodevalue[tn]=tp;
				res=res||tp;
			}
		}
	}
	return res;
} 

int getans(int t1,int t2){ //递归求答案
	int ans=inf;
	string temp=a[t1][t2].first;
	int number=a[t1][t2].second;
	
	bool existtrue=false,existfalse=false;
	int num=a[number].size();
	for(int i=0;i<num;i++){
		int tn=a[number][i].second;
		if(nodevalue[tn]) existtrue=true;
		else existfalse=true;
	}
	if(existtrue&&existfalse) ans=1;
	else{
		for(int i=0;i<num;i++){
			string ts=a[number][i].first;
			if(ts=="AND"||ts=="OR"){
				ans=std::min(ans,getans(number,i));
			}
		}
		if(ans!=inf){
			if(existtrue&&temp=="OR") ans+=1;
			if(existfalse&&temp=="AND") ans+=1; 
		}
	}
	return ans;
}

int main(){
	cin>>n;
	int t;
	string ts;
	for(int i=1;i<=n;i++){
		cin>>t>>ts;
		a[t].push_back(make_pair(ts,i));
	}
	
	nodevalue[1]=prepare(0,0);
	
	int ans=getans(0,0);
	if(ans==inf) ans=-1;
	cout<<ans<<endl;
	return 0;
}

    在看过讨论之后,带入了树形dp的思维,发现如果自底向上的去进行dp确实可以准确的推出每个结点的所统领的子树的最小翻转次数,最终的根节点答案自然也就是准确的最小次数.

    这个超链接是 官方发出的讨论,可以参考一下.

    自底向上这个思维让我恍然大悟然后改写出了以下的解法:

 

#include <iostream>
#include <vector>
using namespace std;

const int maxn=101;
const int inf=(1<<30);

int n;
vector<int> a[maxn];
string nodestring[maxn];

bool nodevalue[maxn];
int f[maxn]; //dp数组,f[i]表示编号为i的结点所统领的子树想要改变值最少需要几次翻转,无法改变的情况被表示为inf
void getans(){
	for(int i=1;i<=n;i++) f[i]=inf; //初始化 
	
	for(int i=n;i>=1;i--){ //从n到1,这很重要
                //先计算结点i的值
		if(nodestring[i]=="TRUE") nodevalue[i]=true;
		else if(nodestring[i]=="FALSE") nodevalue[i]=false;
		else{
			if(nodestring[i]=="AND"){
				int num=a[i].size();
				nodevalue[i]=true;
				for(int j=0;j<num;j++) nodevalue[i]=(nodevalue[i]&&nodevalue[a[i][j]]); 
			}
			else if(nodestring[i]=="OR"){
				int num=a[i].size();
				nodevalue[i]=false;
				for(int j=0;j<num;j++) nodevalue[i]=(nodevalue[i]||nodevalue[a[i][j]]); 
			}

                        //再进行dp推算
			bool existtrue=false,existfalse=false;
			int num=a[i].size();
			for(int j=0;j<num;j++){
				int tn=a[i][j];
				if(nodevalue[tn]) existtrue=true;
				else existfalse=true;
			}
			if(existtrue&&existfalse) f[i]=1;
			else{
				for(int j=0;j<num;j++) f[i]=std::min(f[i],f[a[i][j]]);
				if(f[i]!=inf){
					if(existtrue&&nodestring[i]=="OR") f[i]+=1;
					if(existfalse&&nodestring[i]=="AND") f[i]+=1; 
				}
			}	
		}
	}
}

int main(){
	cin>>n;
	int parent;
	string ts;
	for(int i=1;i<=n;i++){
		cin>>parent>>ts;
		a[parent].push_back(i);
		nodestring[i]=ts;
	}
	
	getans();
	
	if(f[1]==inf) cout<<"-1"<<endl;
	else cout<<f[1]<<endl;

	return 0;
}

 

 

 

点赞