Bootstrap

集训第二天

(昨天自己去整职业规划了,发现计算机要学的东西真的好多啊!!!欲哭无泪呜呜,充满动力和压力 > ^ <)

stack queue priority_queue

A - 后缀表达式

Description

所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。

本题中运算符仅包含 +-*/。保证对于 / 运算除数不为 0。特别地,其中 / 运算的结果需要向 0 取整(即与 C++ / 运算的规则一致)。

如:3*(5-2)+7 对应的后缀表达式为:3.5.2.-*7.+@。在该式中,@ 为表达式的结束符号。. 为操作数的结束符号。

Input

输入一行一个字符串 s,表示后缀表达式。

Output

输出一个整数,表示表达式的值。

Sample 1

InputcopyOutputcopy
3.5.2.-*7.+@
16

Sample 2

InputcopyOutputcopy
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

InputcopyOutputcopy
2*(x+y)/(1-x)@
YES

Sample 2

InputcopyOutputcopy
(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

InputcopyOutputcopy
1+1*3+4
8

Sample 2

InputcopyOutputcopy
1+1234567890*1
7891

Sample 3

InputcopyOutputcopy
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

InputcopyOutputcopy
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

InputcopyOutputcopy
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-堆

 洛谷 - P3378 

Description

给定一个数列,初始为空,请支持下面三种操作:

  1. 给定一个整数 x,请将 x 加入到数列中。
  2. 输出数列中最小的数。
  3. 删除数列中最小的数(如果有多个数最小,只删除 1 个)。

Input

第一行是一个整数,表示操作的次数 n。
接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。

  • 若op=1,则后面有一个整数 x,表示要将 x 加入数列。
  • 若op=2,则表示要求输出数列中的最小数。
  • 若 op=3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。

Output

对于每个操作 2,输出一行一个整数表示答案。

Sample 1

InputcopyOutputcopy
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

InputcopyOutputcopy
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-中位数   

洛谷 - P1168 

Description

给定一个长度为 N 的非负整数序列 A,对于前奇数项求中位数。

Input

第一行一个正整数 N。

第二行 N 个正整数 A1…N​。

Output

共 ⌊2N+1​⌋ 行,第 i 行为A1…2i−1​ 的中位数。

Sample 1

InputcopyOutputcopy
7
1 3 5 7 9 11 6
1
3
5
6

Sample 2

InputcopyOutputcopy
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...再也不熬夜了,我补药近视啊)

 

;