#P2385. 第1题-茶杯
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 1226
            Accepted: 288
            Difficulty: 3
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2023年5月10日-暑期实习
                              
                      
          
 
- 
                        算法标签>栈          
 
第1题-茶杯
思路:模拟栈
什么是栈?
栈是一个先进后出的容器,你可以理解为一个只有一边能够打开的羽毛球筒,那我们想把最里面的羽毛球取出来,就得把所有羽毛球都取出来,这就是栈。
我们需要将给出的 n 个正整数逐一加入到栈中,但是在每次入栈时,如果我们发现有以下两种情况,就需要做出相应的操作:
- 当前数 x 和栈顶 y 是同一个数,那么我们需要取出栈顶数 y ,并将一个新的数 x+y 或者说是 2∗x 加入到栈中
 - 当前数 x 和栈顶开始往下的任意个数(≥2)的数的和相等,那么我们需要将所有这些数都取出来,并把一个新的数 2∗x 加入到栈中
 
由于正整数个数 n 的范围为[1,1000],因此直接模拟栈实现题目所要求的操作即可。
唯一需要注意的是,在将数字合并之后再次压入栈的同时,可能还会触发数字合并的操作,比如说:
当前栈中有3个数,分别为2,4,3。此时,我们需要将一个数3加入到栈中,我们先是触发了第一个情况,于是我们把3取出,得到了一个新的数 2∗3=6 ,并准备将6加入到栈中。这时栈里面有2个数分别为2,4,于是我们又触发了第二个条件,取出2和4,并得到了数字12。
最终,栈里面仅有一个数字,也就是12。
在最坏情况下,在每次压入栈时,都会遍历前面所有数字判断是否有数字(或者一串数字的和)与入栈数字相等(比如一个单调递减的序列),因此该方法的时间复杂度为:O(N2)
代码
C++
#include <iostream>
#include <cstdio>
using namespace std;
#define ll long long
const int maxn=1e3+10;
ll stack[maxn];
int top;
int main(){
	ll x;
	while(scanf("%lld",&x)!=EOF){//读入数据 
		while(1){
		// 存在合并后压入栈再次合并的情况,因此循环判断是否有合并的情况 
			bool flag=false;
			ll tmp=0;
			for(int i=top;i&⊤--i){// 暴力查找是否和为x的数 
				tmp+=stack[i];
				if(tmp==x){// 存在一段和为x,合并,并更新栈 
					x=x+tmp;
					top=i-1;
					flag=true;
					break;
				}
			}
			if(flag==false) 
				break;
		}
		stack[++top]=x;
	}
	while(top){
		printf("%lld ",stack[top]);
		top--;
	}
	
	return 0;
}
python
numbers = list(map(int, input().strip().split()))  # 解析输入
stack = []
for x in numbers:
    while True:
        flag = False
        tmp = 0
        for i in range(len(stack) - 1, -1, -1):  # 逆序遍历栈查找合并
            tmp += stack[i]
            if tmp == x:  # 发现可合并的情况
                x += tmp
                stack = stack[:i]  # 只保留前面的元素
                flag = True
                break
        if not flag:
            break
    stack.append(x)  # 压入栈
print(" ".join(map(str, reversed(stack)))) 
Java
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        final int maxn = 1010;
        long[] stack = new long[maxn];
        int top = 0;
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLong()) {
            long x = scanner.nextLong(); // 读入数据
            while (true) {
                boolean flag = false;
                long tmp = 0;
                for (int i = top; i > 0; i--) { // 暴力查找是否和为x的数
                    tmp += stack[i];
                    if (tmp == x) { // 存在一段和为x,合并,并更新栈
                        x = x + tmp;
                        top = i - 1;
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    break;
                }
            }
            top++;
            stack[top] = x;
        }
        scanner.close();
        while (top > 0) {
            System.out.print(stack[top] + " ");
            top--;
        }
    }
}
Go
package main
import "fmt"
const maxn = 1010
func main() {
	var stack [maxn]int64 // 栈数组
	top := 0 // 栈顶指针
	var x int64
	for {
		_, err := fmt.Scanf("%d", &x) // 读入数据
		if err != nil {
			break
		}
		for {
			flag := false // 标记是否存在合并的情况
			var tmp int64 // 临时变量存储和
			for i := top; i > 0; i-- { // 暴力查找是否和为x的数
				tmp += stack[i]
				if tmp == x { // 存在一段和为x,合并,并更新栈
					x = x + tmp
					top = i - 1
					flag = true
					break
				}
			}
			if !flag {
				break
			}
		}
		top++
		stack[top] = x
	}
	for top > 0 {
		fmt.Printf("%d ", stack[top])
		top--
	}
}
Js
const maxn = 1010; // 最大数组长度
let stack = new Array(maxn); // 栈数组
let top = 0; // 栈顶指针
function main() {
    let input = require('readline-sync'); // 导入readline-sync模块,用于读取用户输入
    
    while (true) {
        let x = parseInt(input.question()); // 读入数据
        if (isNaN(x)) { // 判断输入是否为数字,若不是则退出循环
            break;
        }
        while (true) {
            let flag = false; // 标记是否存在合并的情况
            let tmp = 0; // 临时变量存储和
            for (let i = top; i > 0; i--) { // 暴力查找是否和为x的数
                tmp += stack[i];
                if (tmp === x) { // 存在一段和为x,合并,并更新栈
                    x = x + tmp;
                    top = i - 1;
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                break;
            }
        }
        top++;
        stack[top] = x;
    }
    while (top > 0) {
        process.stdout.write(stack[top] + " "); // 输出栈顶元素并空格分隔
        top--;
    }
}
main(); // 调用主函数执行
        题目内容
有一个叫做小明的人,他非常喜欢喝茶。他有 x 个奇怪的杯子,每个杯子都可以装入一个正整数。小明决定将这些杯子中的个数字压入一个栈中,但他有些规矩:每次他要向栈中压入一个数字,如果栈顶数字与前一个数字相同,他就会将这两个数字取出来相加,并且将它们的和压入栈中。另外,如果栈顶数字等于前面连续 y 个数字的和(1 ≤ y ≤ x),他也会将这 y+1 个数字取出来相加,并且将它们的和压入栈中。当然,如果以上两个规则都不满足,他就不会进行任何操作。现在,小明将一组正整数依次压入栈中,请你告诉他最终栈中剩下的数字是什么。
输入描述
使用单个空格隔开的正整数的字符串,如"5 6 7 8", 左边的数字先入栈。
正整数的范围为[1, 231−1]
正整数个数的范围为[1,1000]。
输出描述
最终栈中存留的元素值,元素值使用的单个空格隔开,如"8 7 6 5", 从左至右依次为栈顶至栈底的数字。
样例1
输入
55 66 121 5 5
输出
10 242
解释:向栈压入 121 时,55 + 66 = 121 ,数据合并后入栈 242 ,压入两个 5 时,合并为 10 ,最终栈顶至栈底的数字为 10 和 242 .
样例2
输入
7 5 8 9 3
输出
3 9 8 5 7
解释:无任何整数合并