(昨天自己去整职业规划了,发现计算机要学的东西真的好多啊!!!欲哭无泪呜呜,充满动力和压力 > ^ <)
stack queue priority_queue
A - 后缀表达式
Description
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。
本题中运算符仅包含 +-*/。保证对于 / 运算除数不为 0。特别地,其中 / 运算的结果需要向 0 取整(即与 C++ /
运算的规则一致)。
如:3*(5-2)+7 对应的后缀表达式为:3.5.2.-*7.+@。在该式中,@
为表达式的结束符号。.
为操作数的结束符号。
Input
输入一行一个字符串 s,表示后缀表达式。
Output
输出一个整数,表示表达式的值。
Sample 1
Inputcopy | Outputcopy |
---|---|
3.5.2.-*7.+@ | 16 |
Sample 2
Inputcopy | Outputcopy |
---|---|
10.28.30./*7.-@ | -7 |
Hint
数据保证,1≤∣s∣≤50,答案和计算过程中的每一个值的绝对值不超过 109。
#include <bits/stdc++.h>
using namespace std;
int main(){
string s;cin >> s;
stack<int> st;//栈
int num = 0;
for(int i = 0; i < s.size()-1; i++){//s.size()-1
if(s[i] == '.'){
st.push(num);//输入.前面的数字
num = 0;
}else if(s[i] >= '0' && s[i] <= '9'){
num = 10*num + s[i] - '0';//把a[i]传给num,第二个num为0
//为了可以传多位数
}else{
int num2 = st.top();st.pop();
int num1 = st.top();st.pop();
if(s[i] == '+') st.push(num1 + num2);
if(s[i] == '-') st.push(num1 - num2);
if(s[i] == '*') st.push(num1 * num2);
if(s[i] == '/') st.push(num1 / num2);
}
}
cout << st.top();//最后只剩一个数
return 0;
}
B - 表达式括号匹配
Description
假设一个表达式有英文字母(小写)、运算符(+
、-
、*
、/
)和左右小(圆)括号构成,以 @
作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则输出 YES
;否则输出 NO
。表达式长度小于 255,左圆括号少于 20 个。
Input
一行:表达式。
Output
一行:YES
或 NO
。
Sample 1
Inputcopy | Outputcopy |
---|---|
2*(x+y)/(1-x)@ | YES |
Sample 2
Inputcopy | Outputcopy |
---|---|
(25+x)*(a*(a+b+b)@ | NO |
Hint
表达式长度小于 255,左圆括号少于 20 个。
思路:
左括号等于右括号
但当))1+2(( 时不成立,所以可以先判断右括号
#include<bits/stdc++.h>
using namespace std;
int a,i,b;
int main(){
char s[300];
cin>>s;
for(i=0;i<strlen(s);i++) {
if(s[i]=='@') break;
if(s[i]==')') b++;
if(b>a){
cout<<"NO";
return 0;
}
if(s[i]=='(') a++;
}
if(a!=b)cout<<"NO";
else{
cout<<"YES";
}
return 0;
}
C - 表达式求值
Background
NOIP2013 普及组 T2
Description
给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。
Input
一行,为需要你计算的表达式,表达式中只包含数字、加法运算符 +
和乘法运算符 *
,且没有括号,所有参与运算的数字均为 0 到 2^31−1 之间的整数。
输入数据保证这一行只有 0123456789+*
这 12 种字符。
Output
一个整数,表示这个表达式的值。
注意:当答案长度多于 4 位时,请只输出最后 4 位,前导 0 不输出。
Sample 1
Inputcopy | Outputcopy |
---|---|
1+1*3+4 | 8 |
Sample 2
Inputcopy | Outputcopy |
---|---|
1+1234567890*1 | 7891 |
Sample 3
Inputcopy | Outputcopy |
---|---|
1+1000000003*1 | 4 |
Hint
对于 30% 的数据,0≤ 表达式中加法运算符和乘法运算符的总数 ≤100。
对于 80% 的数据,0≤ 表达式中加法运算符和乘法运算符的总数 ≤1000。
对于 1100% 的数据,0≤ 表达式中加法运算符和乘法运算符的总数 ≤100000。
思路:
先算乘 最后全加起来
#include<stdio.h>
long long a[1000001];
char c[1000001];
int idx=2,ans;
int main(){
scanf("%d",&a[1]);//a[1]
while(scanf("%c",&c[idx++])!=EOF)// 例c[2]c[4]c[6]
scanf("%d",&a[idx++]);//如a[3]a[5]a[7]
idx-=2;//idx=6,即除去第一个数 剩余的 运算符加上数字总数
for(int i=idx;i>0;i--)//先算完所有乘法
if(c[i]=='*')//c[6]
a[i-1]=(a[i-1]*a[i+1])%10000;//a[5]=a[5]*a[7]
for(int i=1;i<=idx;i++)
if(c[i]=='+')//没有c[1] c[2]
ans=(ans+a[i+1])%10000;//a[3]+a[5]+...
printf("%d",(ans+a[1])%10000);//加上第一个数
}
或者 用 stack
#include<bits/stdc++.h>
using namespace std;
int main(){
char a;
int x;
stack<int >q;
cin>>x;
q.push(x);
while(cin>>a>>x){
if(a=='*'){
x=x*q.top()%10000;
q.pop();
}
q.push(x);
}
int n=0;
while(!q.empty()){
n=(n+q.top())%10000;
q.pop();
}
cout<<n;
}
D - 约瑟夫问题
Description
n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰 n−1 名小朋友,而该题是全部出圈。
Input
输入两个整数 n,m。
Output
输出一行 n 个整数,按顺序输出每个出圈人的编号。
Sample 1
Inputcopy | Outputcopy |
---|---|
10 3 | 3 6 9 2 7 1 8 5 10 4 |
Hint
1≤m,n≤100
#include<iostream>
using namespace std;
#include<queue>
queue<int> q;
int main(){
int m,n,a=1;//a只是一个计数的东西
cin>>m>>n;
for(int i=1;i<=m;i++)
q.push(i);
while(!q.empty()){
if(a==n){//找到了 就踢他
cout<<q.front()<<" ";
q.pop();
a=1;//归位
}
else{//把队列首位移到末位
a++;//记数
q.push(q.front());//返回队首元素的值,但不删除该元素
q.pop();//删除队列首元素
}
}
return 0;
}
E - 圆桌问题
圆桌上围坐着2n个人。其中n个人是好人,另外n个人是坏人。如果从第一个人开始数数,数到第m个人,则立即处死该人;然后从被处死的人之后开始数数,再将数到的第m个人处死……依此方法不断处死围坐在圆桌上的人。试问预先应如何安排这些好人与坏人的座位,能使得在处死n个人之后,圆桌上围坐的剩余的n个人全是好人。
Input
多组数据,每组数据输入:好人和坏人的人数n(<=32767)、步长m(<=32767);
Output
对于每一组数据,输出2n个大写字母,‘G’表示好人,‘B’表示坏人,50个字母为一行,不允许出现空白字符。相邻数据间留有一空行。
Sample
Inputcopy | Outputcopy |
---|---|
2 3 2 4 | GBBG BGGB |
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> table;
int n, m;
while(scanf("%d%d", &n, &m) != EOF){
table.clear(); // 删除上次vector容器中的数据
for (int i = 0; i < 2*n; i++){ // 初始化vector
table.push_back(i);
}
int pos = 0;
for (int i = 0; i < n; i++){
pos = (pos+m-1)%table.size(); // 圆桌,取余防止编号越界
table.erase(table.begin()+pos); // 删除第m个人
}
int j = 0;
for (int i = 0; i < 2*n; i++){ // 对原来的2n个人进行遍历
if (i%50==0 && i != 0){ // 50个人一行
printf("\n");
}
if (i == table[j] &&j < table.size()){ // 留在桌上的人都是好人(即vector容器中的全可输出'G')
printf("G");
j++;
}
else{
printf("B");
}
}
printf("\n\n"); // 注意相邻数据间留有一个空行
}
return 0;
}
F-堆
Description
给定一个数列,初始为空,请支持下面三种操作:
- 给定一个整数 x,请将 x 加入到数列中。
- 输出数列中最小的数。
- 删除数列中最小的数(如果有多个数最小,只删除 1 个)。
Input
第一行是一个整数,表示操作的次数 n。
接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。
- 若op=1,则后面有一个整数 x,表示要将 x 加入数列。
- 若op=2,则表示要求输出数列中的最小数。
- 若 op=3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。
Output
对于每个操作 2,输出一行一个整数表示答案。
Sample 1
Inputcopy | Outputcopy |
---|---|
5 1 2 1 5 2 3 2 | 2 5 |
Hint
【数据规模与约定】
- 对于 30%30% 的数据,保证 n≤15。
- 对于 70%70% 的数据,保证 n≤104。
- 对于 100%100% 的数据,保证1≤n≤106,1≤x<231,op∈{1,2,3}。
#include<bits/stdc++.h>
#define bug(a) cout<<"bug1 : "<<a<<endl;
#define bugg(a,b) cout<<"bug2 : "<<a<<" "<<b<<endl;
using namespace std;
#define endl '\n'
//#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int main()
{
priority_queue<int,vector<int>,greater<int> >mo;
int n,i,j,t;
cin>>n;
for(i=0;i<n;i++)
{
cin>>t;
if(t==1)
{
cin>>j;
mo.push(j);
}
else if(t==2)
{
cout<<mo.top()<<endl;
}
else
{
mo.pop();
}
}
return 0;
}
G-合并果子 / [USACO06NOV] Fence Repair
洛谷 - P1090
Description
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力=3+12=15 。可以证明 15 为最小的体力耗费值。
Input
共两行。
第一行是一个整数n(1≤n≤10000) ,表示果子的种类数。
第二行包含 n 个整数,用空格分隔,第 i 个整数 ai(1≤ai≤20000) 是第 i 种果子的数目。
Output
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231 。
Sample 1
Inputcopy | Outputcopy |
---|---|
3 1 2 9 | 15 |
Hint
对于 30% 的数据,保证有 n≤1000:
对于 50% 的数据,保证有 n≤5000;
对于全部的数据,保证有 n≤10000。
#include <bits/stdc++.h>
using namespace std;
long long a[1000005];
long long n,ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
sort(a+i,a+n+1);
a[i+1]+=a[i];
ans+=a[i+1];
}
cout<<ans;
}
看不懂上面的看下面的
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;cin >> n;
long long int a[100110];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
long long int ret = 0;
while (n > 1) {
sort(a, a+n);
a[1] += a[0];
if (n != 2) ret += a[1];
for (int i = 1; i < n; i++) {
a[i-1] = a[i];
}
n--;
}
cout << a[0]+ret;
return 0;
}
H-中位数
Description
给定一个长度为 N 的非负整数序列 A,对于前奇数项求中位数。
Input
第一行一个正整数 N。
第二行 N 个正整数 A1…N。
Output
共 ⌊2N+1⌋ 行,第 i 行为A1…2i−1 的中位数。
Sample 1
Inputcopy | Outputcopy |
---|---|
7 1 3 5 7 9 11 6 | 1 3 5 6 |
Sample 2
Inputcopy | Outputcopy |
---|---|
7 3 1 5 9 8 7 6 | 3 3 5 6 |
Hint
对于 20% 的数据,N≤100;
对于40% 的数据,N≤3000;
对于 100% 的数据,1≤N≤100000,0≤Ai≤109。
思路:
左边大根堆,右边小根堆,第一个数放中间,为第一个中位数;后面数据两个两个为一组读入;与此同时判断左右边大小
#include <bits/stdc++.h>
using namespace std;
int main() {
int N;cin >> N;
priority_queue<int, vector<int>, less<int> > left;
priority_queue<int, vector<int>, greater<int> > right;
int a;cin >> a;
left.push(a);
cout << left.top() << endl;
int t = (N + 1) / 2 - 1;
while (t--) {
int x, y;//每次读入两个数
cin >> x >> y;//读入的数小于原来的中位数,则放入到右侧小顶堆
if (x <= left.top()) {
left.push(x);
} else {
right.push(x);
}
if (y <= left.top()) {
left.push(y);
} else {
right.push(y);
}//如果左边堆的大小减一之后还比右堆大,说明此次操作把 x 和 y都push进了left堆,需要维护两个堆的大小关系
if (left.size() - 1 > right.size()) {
int temp = left.top();
left.pop();
right.push(temp);
}//如果右边堆的大小比左堆大,说明此次操作把 x 和 y都push进了right堆,需要维护两个堆的大小关系
if (right.size() > left.size()) {
int temp = right.top();
right.pop();
left.push(temp);
}
cout << left.top() << endl;
}
return 0;
}
(final...再也不熬夜了,我补药近视啊)