CSP-J 2020 表达式

题目地址:https://www.luogu.com.cn/problem/P7073

第一次写想着直接用栈一步一步处理,但是只有五十分
这个题大致可以分为三步
1.输入的第一行数据数据处理为字符数组
2.输入的整数与数组里对应的数字对应上
3.根据输出需要改变的位置 处理表达式

50分写法,测试点会超时

#include<bits/stdc++.h>
using namespace std;
char a[1000005][10];
struct ST
{
	//i 为在b数组的位置,v为其对应值 j为在a数组的位置
	int i,v,j;
}st[1000005];

//两个栈,栈1用于存储数据,栈2用于运算数据
//zp用于临时存储
int z1[1000005],z2[1000005];
int zp[1000005];
int main()
{
	int n;
	//i 用于遍历输入的所有字符串
	//j 用于遍历字符串a
	int i=0,j=1;
	char *fh=new char[10];
	//将输入的第一行字符串处理
	while(1)
	{
		//输入字符 
		char c=getchar();
		//输入回车,结束循环
		if(c=='\n')
		{
			//添加终止符
			fh[i]=0;
			i=0;
			//字符串复制到a里
			strcpy(a[j++],fh);
			break;
		}
		//输入空格  则前面数据为一个完整单词
		else if(c==' ')
		{
			//添加终止符
			fh[i]=0;
			i=0;
			//字符串复制到a里
			strcpy(a[j++],fh);
			continue;
			
		}
		else
		{
			fh[i++]=c;
		}
		
	}
	fh=nullptr;
	
	cin>>n;
	for(i=1;i<=n;i++)
	{
		int t;
		cin>>t;
		st[i].v=t;
		st[i].i=i;
	}
	int x=1;
	
	//数据全部进入z1栈 字符就以整数形式 处理
	for(i=1;i<j;i++)
	{
		//将带x的 数据位置找到,运算处理
		if(a[i][0]=='x')
		{
			int t=0;
			for(int z=1;z<strlen(a[i]);z++)
			{
				t=10*t+a[i][z]-'0';
			}
			
			st[t].j=i;
			z1[x++]=st[t].v;
		}
		else
		{
			z1[x++]=a[i][0];
		}
	}
	
	cin>>n;
	while(n--)
	{
		int k=1;
		for(i=1;i<x;i++)
		{
			zp[i]=z1[i];
		}
		int t;
		cin>>t;
		zp[st[t].j]=!zp[st[t].j];
		//运算处理
		for(int i=1;i<j;i++)
		{
			if(zp[i]==1||zp[i]==0)
			{
				z2[k++]=zp[i];
			}
			else
			{
				if(zp[i]=='!')
				{
					z2[k-1]=!z2[k-1];
				}
				else if(zp[i]=='|')
				{
					if(z2[k-1]+z2[k-2]>=1)
					{
						z2[k-2]=1;
					}
					else 
					{
						z2[k-2]=0;
					}
					k--;
				}
				else
				{
					if(z2[k-1]*z2[k-2]==1)
					{
						z2[k-2]=1;
					}
					else
					{
						z2[k-2]=0;
					}
					k--;
				}
			}
		}
		cout<<z2[1]<<endl;
		memset(z2,0,x*4);
		memset(zp,0,x*4);
	}
}

思来想去想了好久,不知道怎么解决,想不到优化方法,看来一下题解,题解说用树存储,一下子豁然开朗,花了两个小时调整了一下代码,ac了。

树存储的要点:
1.叶子节点为数值,其余节点为符号
2.每个节点提前存储当前节点下所有运算出的值,运算时直接拿出来用
3.从修改的目标值往上依次修改,并判断val与nval是否相等(也就是有没有接着运算下去的必要,如果数值修改并不影响当前或者后续某一步的运算结果,则可以直接返回最终数值)

AC写法

#include<bits/stdc++.h>
using namespace std;
char a[1000005][10];
int j=1;
struct Tree{
	//type 为1 表示数字,type为2 表示|,type为3 表示&  type为4表示!
	int type;
	//整数存储对应的数值,字符存储其运算结果
	int val,nval;
	//左右节点和父节点
	int  l=0,r=0,p=0;
	int i;
}tr1[1000005];
struct ST
{
	//i 为在b数组的位置,v为其对应值 j为在a数组的位置
	int i,v,j;
}st[1000005];
//tr2栈1用于临时存储数据,方便生成树
Tree tr2[1000005];
int sc_tr(Tree *tree,int k)
{
	Tree &t=tree[k];
	//如果只有一个节点 则直接返回 
	if(t.type==1&&t.p==0)
	{
		return t.nval;
	}
	else
	{
		//先运算,运算完成 子节点还原
		if(t.type==4)
		{
			t.nval=!(tree[t.l].nval);	
		}
		else if(t.type==2)
		{
			t.nval=(tree[t.l].nval)|(tree[t.r].nval);		
		
		}
		else if(t.type==3)
		{
			t.nval=(tree[t.l].nval)&(tree[t.r].nval);
		}
		
		tree[t.r].nval=tree[t.r].val;
		tree[t.l].nval=tree[t.l].val;
	//加上判断 如果当前节点值没修改 则直接返回
		if(t.val==t.nval)
		{
			return tree[j-1].nval;
		} 
	//	cout<<k<<" "<<t.type<<" "<<t.p<<" "<<t.val<<" "<<t.nval<<endl;
		if(t.p!=0)
		{
			return sc_tr(tree,t.p);
		}
		
	}
	return t.nval;
}
int main()
{
	int n;
	//i 用于遍历输入的所有字符串
	//j 用于遍历字符串a
	int i=0;
	char *fh=new char[10];
	//将输入的第一行字符串处理
	while(1)
	{
		//输入字符 
		char c=getchar();
		//输入回车,结束循环
		if(c=='\n')
		{
			//添加终止符
			fh[i]=0;
			i=0;
			//字符串复制到a里
			strcpy(a[j++],fh);
			break;
		}
		//输入空格  则前面数据为一个完整单词
		else if(c==' ')
		{
			//添加终止符
			fh[i]=0;
			i=0;
			//字符串复制到a里
			strcpy(a[j++],fh);
			continue;
		}
		else
		{
			fh[i++]=c;
		}
		
	}
	
	cin>>n;
	//输入的数据与值一一对应
	for(i=1;i<=n;i++)
	{
		int t;
		cin>>t;
		st[i].v=t;
		st[i].i=i;
	}
	int x=1;
	//数据全部进入z1栈 字符就以整数形式 处理
	for(i=1;i<j;i++)
	{
		//将带x的 数据位置找到,运算处理
		if(a[i][0]=='x')
		{
			int t=0;
			for(int z=1;z<strlen(a[i]);z++)
			{
				t=10*t+a[i][z]-'0';
			}		
			tr1[x].val=st[t].v;
			tr1[x].type=1;
			tr1[x].i=i;
			st[t].j=i;
			x++;
			
			tr2[i].val=st[t].v;
			tr2[i].type=1;
			tr2[i].i=i;
		}
		else
		{
			Tree tree;
			tree.i=i;
			
			if(a[i][0]=='!')
			{
				//节点赋值
				tree.l=tr1[x-1].i;
				tr2[tree.l].p=tree.i;
				
				tree.type=4;
				tree.val=!(tr2[tree.l].val);
				//栈头更换
				tr1[x-1]=tree;
				
			}
			else 
			{
				//子节点处理
				tree.l=tr1[x-2].i;
				tree.r=tr1[x-1].i;
				tr2[tree.l].p=tree.i;
				tr2[tree.r].p=tree.i;
				
				
				if(a[i][0]=='|')
				{
					tree.type=2;
					tree.val=tr2[tree.l].val|tr2[tree.r].val;
				}
				else
				{
					tree.type=3;
					tree.val=tr2[tree.l].val&tr2[tree.r].val;
				}
				//栈头更换
				tr1[x-2]=tree;
				x--;
			}
			
			//入树
			tr2[i]=tree;
			
		}
		tr2[i].nval=tr2[i].val;
	}
	
	//变量n继续使用 避免变量名过多
	cin>>n;
	while(n--)
	{
		int t;
		scanf("%d",&t);
	
		//用另一个参数nval来代替更改值 
		tr2[st[t].j].nval=!(tr2[st[t].j].val);
		int f=sc_tr(tr2, st[t].j);
		printf("%d\n",f);
		//数据还原
		tr2[j-1].nval=tr2[j-1].val;

	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值