首页 > 其他分享 >题解 P2229 【[HNOI2002]沙漠寻宝】

题解 P2229 【[HNOI2002]沙漠寻宝】

时间:2023-07-25 12:34:02浏览次数:54  
标签:case return int 题解 top pop HNOI2002 P2229 s2

posted on 2021-06-01 12:15:15 | under 题解 | source

这题一看就知道是个模拟。

做模拟题的时候,一定要先确保你的程序能跑出正确的结果,再去想优化时间。

这道题还是很简单的,让我们开始吧:

读入

我们把输入离线,拿 string 存起来。如果不离线,那 loop 就会很难处理,加大难度。

int n;
string a[1010];//亲测最多只会有 1000 条语句

while(cin>>a[++n]);n--;

预处理

我们可以先预处理每个 startloop 对应的 end 的位置。这样做的原因会在后面说。

int jump[1010];
stack<int> init;
for(int i=1;i<=n;i++){
	if(a[i]=="start"||a[i]=="loop") init.push(i);//如果是 start/loop,把编号压入栈
	if(a[i]=="end") jump[init.top()]=i,init.pop();//如果是 end,标记位置
}

运行代码

定义一个 int run(int now) 函数,使用递归实现,\(now\) 表示从哪条语句开始运行,返回值表示最终执行到哪条语句。

但我们要考虑一个问题:遇到 break 该返回什么呢?我们可以返回 \(-1\),然后特判一下。

int run(int now){
	for(int i=now;i<=n;i++){
		if(a[i]=="write"){
        //write:输出一个表达式的值并换行
        //那就输出 a[i+1] 这个表达式的值咯
			i++;
			cout<<calc(a[i])<<endl;//这个 calc 是计算函数,后面讲
		}else if(a[i]=="loop"){
        //loop:循环语句,执行 a[i+1] 次
        //那就递归实现
			i++;
			int times=calc(a[i]);
			for(int j=1;j<=times;j++){
				int tmp=run(i+1);
				if(tmp==-1) break;//返回 -1 说明是 break,外层 loop 也跟着 break 
			}
			i=jump[i-1];//jump 的作用,跳到这个 loop 后面
		}else if(a[i]=="break"){
			return -1;//返回 -1,代表自己是 break
		}else if(a[i]=="continue"){ 
			return jump[now-2];//返回这个这个 loop 对应的 end 的位置
		}else if(a[i]=="end"){
			return i;//同 continue
		}else if(a[i]=="start"){
			//什么也不做哈哈哈
		}else{//都不是,那就是赋值语句
			int tmp=calc(a[i].substr(2));//substr(2) 表示从 2 这个位置后面的所有字符
			m[a[i][0]-'a']=tmp;//赋值
		}
	}
	return n;
}

计算表达式

这部分可以看 link,照着他的代码打一遍就好了。

int lev(char a){//优先级
	switch(a){
		case '(':return 0;
		case '+':case '-':return 1;
		case '*':case '/':return 2;
		case ')':return 3;
	}
	return 0;
}
void plu(stack<int> &s,char op){//需要注意,plus 是 STL 函数
	int num1,num2;
	num2=s.top(),s.pop();
	num1=s.top(),s.pop();
	switch(op){
		case '+':s.push(num1+num2);return ;
		case '-':s.push(num1-num2);return ;
		case '*':s.push(num1*num2);return ;
		case '/':s.push(num1/num2);return ;
	}
}
int calc(string a){//计算函数
	stack<int> s1,s2;
	for(int i=0;i<a.size();i++){
		if('a'<=a[i]&&a[i]<='z') s1.push(m[a[i]-'a']);//处理变量
		else if('0'<=a[i]&&a[i]<='9'){//数字
			int tmp=a[i]-48,j=i+1;
			while(j<a.size()&&'0'<=a[j]&&a[j]<='9') tmp=tmp*10-48+a[j],j++;
			s1.push(tmp),i=j-1;
		}else if(a[i]=='+'||a[i]=='-'||a[i]=='*'||a[i]=='/'){//加减乘除
			if(s2.empty()) s2.push(a[i]);
			else{
				while(!s2.empty()&&lev(s2.top())>=lev(a[i])){
					plu(s1,s2.top());
					s2.pop();
				}
				s2.push(a[i]);
			}
		}else{//括号
			if(a[i]=='(') s2.push(a[i]);
			else{
				while(s2.top()!='('){
					plu(s1,s2.top());
					s2.pop();
				}
				s2.pop();
			}
		}
	}
	while(!s2.empty()){
		plu(s1,s2.top());
		s2.pop();
	}
	return s1.top();
}

整合

把上面的代码结合在一起就能 A 了。

完整代码:

#pragma GCC optimize(2)
//这篇题解常数很大,会 TLE on #5,需要 O2 优化 
#include <stack>
#include <string>
#include <iostream>
#include <queue>
using namespace std;
string a[1010];
int n,m[30],jump[1010];
int lev(const char &a){
	switch(a){
		case '(':return 0;
		case '+':case '-':return 1;
		case '*':case '/':return 2;
		case ')':return 3;
	}
	return 0;
}
void plu(stack<int> &s,const char &op){
	int num1,num2;
	num2=s.top(),s.pop();
	num1=s.top(),s.pop();
	switch(op){
		case '+':s.push(num1+num2);return ;
		case '-':s.push(num1-num2);return ;
		case '*':s.push(num1*num2);return ;
		case '/':s.push(num1/num2);return ;
	}
}
int calc(const string &a){
	stack<int> s1,s2;
	for(int i=0;i<a.size();i++){
		if('a'<=a[i]&&a[i]<='z') s1.push(m[a[i]-'a']);
		else if('0'<=a[i]&&a[i]<='9'){
			int tmp=a[i]-48,j=i+1;
			while(j<a.size()&&'0'<=a[j]&&a[j]<='9') tmp=tmp*10-48+a[j],j++;
			s1.push(tmp),i=j-1;
		}
		else if(a[i]=='+'||a[i]=='-'||a[i]=='*'||a[i]=='/'){
			if(s2.empty()) s2.push(a[i]);
			else{
				while(!s2.empty()&&lev(s2.top())>=lev(a[i])){
					plu(s1,s2.top());
					s2.pop();
				}
				s2.push(a[i]);
			}
		}else{
			if(a[i]=='(') s2.push(a[i]);
			else{
				while(s2.top()!='('){
					plu(s1,s2.top());
					s2.pop();
				}
				s2.pop();
			}
		}
	}
	while(!s2.empty()){
		plu(s1,s2.top());
		s2.pop();
	}
	return s1.top();
}
int run(const int &now){
	for(int i=now;i<=n;i++){
		if(a[i]=="write"){
			i++;
			cout<<calc(a[i])<<endl;
		}else if(a[i]=="loop"){
			i++;
			int times=calc(a[i]);
			for(int j=1;j<=times;j++){
				int tmp=run(i+1);
				if(tmp==-1) break;
			}
			i=jump[i-1];
		}else if(a[i]=="break"){
			return -1;
		}else if(a[i]=="continue"){
			return jump[now-2];
		}else if(a[i]=="end"){
			return i;
		}else if(a[i]=="start"){
			//f**k ccf
		}else{
			int tmp=calc(a[i].substr(2));
			m[a[i][0]-'a']=tmp;
		}
	}
	return n;
}
stack<int> init;
int main(){
    ios::sync_with_stdio(0),cin.tie(0);
	while(cin>>a[++n]);n--;
	for(int i=1;i<=n;i++){
		if(a[i]=="start"||a[i]=="loop") init.push(i);
		if(a[i]=="end") jump[init.top()]=i,init.pop();
	}
	run(1);
	return 0;
}

标签:case,return,int,题解,top,pop,HNOI2002,P2229,s2
From: https://www.cnblogs.com/caijianhong/p/Solution-p2229.html

相关文章

  • 题解 P2903 【[USACO08MAR]The Loathesome Hay Baler S】
    postedon2021-05-0320:50:49|under题解|source首先输入,记录一下哪个齿轮的位置在\((0,0)\),哪个在\((x_t,y_t)\)。接着,为了避免多次判断两个齿轮相切而超时,我们可以预处理一个\(link_{i,j}\),表示第\(i\)个齿轮是否和第\(j\)个齿轮相切。这部分直接\(O(n^2)\)暴......
  • 题解 P1538 【迎春舞会之数字舞蹈】
    postedon2021-06-0113:24:05|under题解|source给\(0\cdots9\)每个数字打表,打它在相应的位置有没有一划。然后把每个数字分成\(5\)部分,暴力输出即可。#include<cstdio>#include<cstring>usingnamespacestd;constchar*db[]={"-||||-","||"......
  • 【大联盟】20230714 T1 三分网络(tri) 题解 CF1666K 【Kingdom Partition】
    题目描述here。题解赛时得分:\(30/30\),想了很久网络流最后不会。感觉这题就纯纯对脑洞,因为把题目中的\(2\)改成\(3\)就做不了)))不过还是相当有意思的。考虑如下建模方式:首先,考虑最小割。对于每个点\(i\),我们用两个点\(x_{i}\),\(y_i\)来表示。\(x_i\)表示\(i\)号点是......
  • 题解 [SDOI2009] HH的项链
    题目链接对于这类问区间不同数的总数,显然是不能用线段树直接维护的,毕竟不符合区间区间可加性。考虑对于一个右端点固定的询问,哪些数字实际上是有权值的。比如区间1332312,显然,实际上对于相同的数字,只有一个是有权值的,其他权值均为\(0\)。但是这样还是无法起到简化的作用......
  • 题解 链表 (chain)
    题目链接首先考虑没有修改怎么做。两种做法。想到询问的形式为保留\(\gek\)的连通块个数,那么先将全部数字按照权值排序,然后从后往前做一遍并查集,并同时统计连通块的数量,在询问时只需二分找到第一个\(\gek\)的位置,将这个位置的答案输出即可。注意考虑答案为\(0\)的情况......
  • 【大联盟】20230713 T1 方向矩阵(rect) 题解 CF1666A 【Admissible Map】
    题目描述here。题解赛时得分:60/100。想到了正解,但调不出来,就改写暴力了。。。首先,我们把问题转化成每个点都入度为\(1\)。我们考虑合法子串只有两种形式:注意到U和D,要么同时出现,要么同时不出现,因为如果存在U,就说明U所在这一行得到度数减少了,一定需要上一行D来弥补......
  • luogu P3203 [HNOI2010] 弹飞绵羊 题解
    题目传送门:P3203[HNOI2010]弹飞绵羊题意\(n\)个数,满足\(i<a_i≤N+1\),\(m\)次下面两种操作修改一个数\(a_i\)从\(x\)开始跳,\(x=a_x\),几次能够跳出序列\(n\leq2*10^5,m<10^5\)分析数据范围比较大,单纯搜索模拟肯定会T,那么考虑每次让他跳一段就......
  • 题解 P7971【[KSN2021] Colouring Balls】
    postedon2022-10-0819:07:28|under题解|sourceproblem交互库有一个长为\(n\)的颜色序列,你可以询问区间\([l,r]\)中有多少种颜色,最后还原交互库手中的序列,只需要保持相对顺序不变。\(n\leq10^3\),最多询问次数\(Q=2000\)或\(Q=10^4\)。solution令\(C\)为\([1......
  • CF1051G Distinctification题解
    link首先可以发现,题目给定的两种操作为我们提供了“反悔机制”,所以有:结论\(1\):即任何一个可以到达的局面都能到达最优解。利用这个结论,首先我们先去重。继续提炼性质,与相差不到\(1\)的数为基准\(+1\)与\(-1\)操作,将这个数的变化范围限制在了一个区间,并且这个区间的数能......
  • 题解 Codeforces Round 887 (Div 1+Div 2) / CF1853AB,CF1852ABCD
    下大分!悲!Div1只过了1A!!!但还是补完整场Div2吧。A.Desortinghttps://codeforces.com/problemset/problem/1853/Aproblem用操作:\([1,i]++,[i+1,n]--\),使得数组不单调不降,求最小操作次数。\(n\leq10^5\)。solution操作等同于在差分数组上选出\(i\),做\(c_1:=c_1+1,c_i:......