【vijos】1765 Caculator(代码题)

https://vijos.org/p/1765

这题用白书的方法是n^2的,所以我tle了一个点。。sad。

我稍微优化了一下。

这个题给我最大的感受不是这个题本身,而是我感受到了自己思考以后并认真查错一次提交获得这么高分的感受。

做题一定要认真、仔细。

写这些题最重要的就是仔细,一些细节方面没处理好,那么就会挂了,而陷入调试的泥潭。

想想我以前写的程序,哪个不是这样呢?

首先一定要有个大概的框架了且自己反复论证这样做是正确的(不考虑满分的情况下,可以充当暴力以便对拍和骗分)

一定要反复论证!

然后是在自己写的时候一定要专心,且一定要跟着框架走,如果发现自己之前想的是错的,一定先离开键盘,仔细的再想一遍。

当写完是否庞大的码量后,一定要静态查错,无疑是几个地方:变量名、数组、逻辑、sb错,且一定要反复验证自己代码的逻辑正确性。

之后才测样例。

一般是直接过的。

如果不过那么不要陷入调试的泥潭,一定要重新看一下哪里会错,哪里可能会错。然后对应的在那里调试,而对应的在那里调试那么就要看自己代码的分块性了,分块得好那么就易于调试。

好了。。

以后就这样,不要着急,要知道这是自己在战斗,只是为了解决问题,而不是一昧地去追求代码的美观,而是正确性。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <cassert>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
#define printarr1(a, b) for1(_, 1, b) cout << a[_] << ‘\t‘; cout << endl
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }

map<string, int> mp;
typedef long long ll;
const int N=105, M=100005;
const ll eps=1e9;
int n, m, ID, a[N], flag, tot, db;
char s[15];
struct node { node *l, *r; bool f, opt; int s; node() { l=r=NULL; f=opt=0; s=0; } }*root=NULL;
struct dat { int s; bool f, opt; dat() { s=0; f=opt=0; } }b[M];
int getid(char *st, int len) {
	string s(st, len);
	if(mp[s]) return mp[s];
	mp[s]=++ID;
	return ID;
}
ll cal(node *x) {
	if(x==NULL || flag) return 0;
	if(!x->opt) {
		if(x->f) return a[x->s];
		return x->s;
	}
	ll l=cal(x->l), r=cal(x->r), ret=0;
	int opt=x->s;
	if(flag) return 0;
	if(opt==‘/‘ && r==0) { flag=1; return 0; }
	if(opt==‘+‘) ret=l+r;
	if(opt==‘-‘) ret=l-r;
	if(opt==‘*‘) ret=l*r;
	if(opt==‘/‘) ret=l/r;
	if(abs(ret)>eps) { flag=1; return 0; }
	return ret;
}
void build(int l, int r, node *&x) {
	// if(db>1) return;
	// ++db;
	// assert(l<=r);
	if(l==r) {
		x=new node();
		x->f=b[l].f;
		x->s=b[l].s;
		return;
	}
	int p=0, c1=-1, c2=-1;
	for1(i, l, r) {
		if(!b[i].opt) continue;
		if(b[i].s==‘(‘) ++p;
		else if(b[i].s==‘)‘) --p;
		else if(!p && (b[i].s==‘+‘ || b[i].s==‘-‘)) c1=i;
		else if(!p && (b[i].s==‘*‘ || b[i].s==‘/‘)) c2=i;
	}
	if(c1==-1) c1=c2;
	if(c1==-1) { build(l+1, r-1, x); return; }
	//puts("here"); dbg(c1);
	x=new node();
	build(l, c1-1, x->l); build(c1+1, r, x->r);
	x->opt=1;
	x->s=b[c1].s;
}
int main() {
	read(n); read(m);
	for1(i, 1, n) {
		scanf("%s", s);
		getid(s, strlen(s));
	}
	char c=getchar(); while(c==‘\n‘||c==‘\r‘||c==‘ ‘) c=getchar();
	while(c!=‘\n‘) {
		if(c>=‘0‘ && c<=‘9‘) {
			int r=0;
			while(c>=‘0‘ && c<=‘9‘) r=r*10+c-‘0‘, c=getchar();
			b[++tot].s=r;
		}
		else if((c>=‘A‘ && c<=‘Z‘) || (c>=‘a‘ && c<=‘z‘)) {
			int len=0;
			while((c>=‘A‘ && c<=‘Z‘) || (c>=‘a‘ && c<=‘z‘)) s[len++]=c, c=getchar();
			s[len]=‘\0‘;
			b[++tot].s=getid(s, len);
			b[tot].f=1;
		}
		else if(c!=‘\n‘) {
			b[++tot].opt=1;
			b[tot].s=c;
			c=getchar();
		}
	}
	build(1, tot, root);
	while(m--) {
		for1(i, 1, n) read(a[i]);
		flag=0;
		int ans=cal(root);
		if(flag) puts("Error!");
		else printf("%d\n", ans);
	}
	return 0;
}

描述

计算器是生活中的常用计算工具,可以用来计算各种各样的代数式的值。不过它是有些缺点的,一个主要的问题是它只支持常量的运算。对于同样形式的代数式,仅仅是因为常量的不同就需要重新输入一遍。

程序员们则可以使用高级语言中的表达式求值。在Pascal/C++语言中,表达式是一个很基础的概念。表达式中可以出现变量、常量、运算符、括号,甚至各种各样的函数。

这里我们仅考虑这样的表达式:它由52个大小写英文字母、数字‘0‘~‘9‘,和’(’、’)’、’+’、’-’、’*’、’/‘这些符号组成。每 个运算符都只执行双目运算符的功能,即它的左右两侧都必须是可计算的量。特别的,这里使用‘/‘符号表示整除。而连续的英文字母组成的单词则表示变量名, 且变量名仅由字母组成。为了简化问题,表达式中的常量均为绝对值不超过10^9的自然数,且无前导0。表达式的计算方法和在源程序中是一样的,即小括号优 先级最高、而‘*‘、‘/‘运算符优先级高于‘+‘、‘-’运算符,同一优先级从左到右按顺序计算。

你的任务,就是对于一个给定的表达式,实现它的计算功能。我们将声明N个变量,并在表达式中引用这些变量。然后给出T组数据。每组数据都是N个变量的一组取值。你需要对每组数据计算此时表达式的返回值。

格式

输入格式

输入文件的第一行包括两个整数N, T,依次表示表达式中引用的变量的数目,以及N个变量的需要计算的取值组数。

接下来N行,每行一个由英文字母组成的字符串,依次表示每个变量名。

接下来一行,一个字符串,表示表达式的内容。

接下来T行,每行N个整数,依次表示每个变量这时的取值。

输出格式

输出文件包括T行,第i行表示N个变量取输入的第i组数值时表达式的答案。

如果在运算过程中出现整数被0除,或者运算过程的中间结果的绝对值大于10^9,输出”Error!”;否则输出一个整数,表示运算结果。运算的中间结果指从左到右按优先级顺序计算时得到的每个数值。

样例1

样例输入1[复制]

2 3
x
total
1+(total/x)
1 3
2 155
0 0

样例输出1[复制]

4
78
Error!

限制

1s

提示

20%的数据满足,表达式中没有小括号。

另外20%的数据满足,表达式中没有加法或减法运算。

另外20%的数据满足,表达式中没有乘法或除法运算。

80%的数据(包括上述在内)满足,表达式的长度不超过100。

100%的数据满足,1 <= N <= 100,1 <= T <= 10,变量名长度不超过10,表达式长度不超过100,000。变量的取值和表达式中的常量是绝对值不超过10^9的自然数。所有变量名互不相同。

时间: 2024-10-29 19:06:59

【vijos】1765 Caculator(代码题)的相关文章

面试题收集-java面试题及答案(基础题122道,代码题19道)

JAVA相关基础知识1.面向对象的特征有哪些方面?1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一是过程抽象,二是数据抽象.2.继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的一个新类可以从现有的类中派生,这个过程称为类继承.新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类).派

Java面试题及答案(基础题122道,代码题19道)

转载自:http://www.blogjava.net/fanyingjie/archive/2007/06/27/126467.aspx JAVA相关基础知识1.面向对象的特征有哪些方面 1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一是过程抽象,二是数据抽象.2.继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的

java常见面试题及答案(基础题122道,代码题19道)

JAVA相关基础知识1.面向对象的特征有哪些方面 1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一是过程抽象,二是数据抽象.2.继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的一个新类可以从现有的类中派生,这个过程称为类继承.新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类).派

java面试题及答案(基础题122道,代码题19道)(转)

JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一是过程抽象,二是数据抽象. 2.继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的一个新类可以从现有的类中派生,这个过程称为类继承.新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父

代码题

一.如何去小数点前两位,并四舍五入? double d=1256.22d; d=d/100; System.out.println(Math.round(d)*100); 二.如何格式化日期? Import java.text.SimpleDateFormat; SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Date dat=new Date(); String str=sdf.format(dat)

代码题(19)— 组合与排列

1.77. 组合 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] class Solution { public: vector<vector<int>> combine(int n, int k) { vector<vector<int>> res; vector<int>

代码题(37)— 螺旋矩阵

1.54. 螺旋矩阵 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素. 示例 1: 输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] 输出: [1,2,3,6,9,8,7,4,5] 示例 2: 输入: [ [1, 2, 3, 4], [5, 6, 7, 8], [9,10,11,12] ] 输出: [1,2,3,4,8,12,11,10,9,5,6,7]    思想:用左上和右下的坐标定位出一次要旋

快手面试代码题

题一 leetcode 168 & 171: A,B,C,...,Z,AA,AB,...,AZ,BA,...,ZZZ,AAAA.... 求第i个字符串 string solve(int num) { string ans; while(num--) { // 将0-25映射到'A'-'Z' int val = num%26; ans += 'A'+val; num /= 26; } reverse(ans.begin(), ans.end()); return ans; } 题二 已知a数组大小

2019字节跳动面试时手撕代码题(持续更新~)

1. N 阶乘末尾0的个数. 输入描述: 输入为一行,n(1 ≤ n ≤ 1000) 输出描述: 输出一个整数,即题目所求解法:要判断末尾有几个0就是判断可以整除几次10.10的因子有5和2,而在0~9之间5的倍数只有一个,2的倍数相对较多,所以本题也就转换成了求N阶乘中有几个5的倍数.也就是每多出来一个5,阶乘末尾就会多出来一个0,这样n / 5就能统计完第一层5的个数,依次处理,就能统计出来所有5的个数.同一个思想两种写法. 题解: 要判断末尾有几个0就是判断可以整除几次10.10的因子有5