假设某地区持续降雨,降落的雨水落入有规则的地坑中,形成水坑,如下图所示的断面图为例:
题目:
输入:用"/"和"\"代表地形断面图中的斜面,用"_"代表平面。在第一行之内完成输入。例如上图中通过字符串: \\///\\_/\\/\\\\/_/\\///输入。
输出:第1行输出该地区积水处横截面的总面积A(正数)
第2行从左至右按顺序输出积水处的数量k,以及各积水处的横截面积Li(i=1,2,......,k),相邻数据用空格隔开
限制:1<=字符串的长度<=2000
输入示例: 输出示例:
\\///\\_/\\/\\\\/_/\\/// 26
4 4 2 1 19
思路:拿到这题,一道求面积的题目,虽说积水处的形状都是规则的,有三角形,梯形的,并且相关的求规则形状的面积的公式我们都知道,但是我们并不能这样求解。
如果仔细点,其实我们这里其实可以发现求面积的一个技巧,比如给的示例中的第一个坑:
这里我们给将每个"\"和"/"都定义为墙壁并给每个墙壁定义一个下标,这样的话求面积我们就很简单了,第一个坑有两层,最底下的一层面积为1=2-1,第二层的面积为3=3-0.我这样写其实就已经发现了规律,就是用每层对应的左右墙壁的下标之差作为该层的面积,每个坑只需要将多层的面积给相加起来即可。
那么如何在众多的"/"和"\"中找到相对应的“另一半”?不知道大家原来有没有做到过一个判断算数表达式中括号是否匹配的题目,使用一个栈,将左括号压入栈,并在遇到一个右括号时弹出栈顶的左括号,判断是否匹配,这里我们类比也使用同样的方法。
解决了求解面积的问题,下面就求解水坑数,其实这里就比较麻烦的了,因为一个水坑可能就是一个简单的小水坑,比如上图中的面积为4,2,1的三个简单的小水坑,而我们也不能遗忘掉复杂水坑,就是多个小水坑加上水坑上层的连接水坑构成的大水坑,例如示例中面积为19的水坑,是两个面积分别为5,4的两个小水坑加上面积为10的连接水层构成的,这样我们该如何求解呢,我们可以考虑再使用一个栈,用来存放一个键值对(key,value)表示一个水坑,key表示一个水坑最左端墙壁的的下标,value是这个水坑的面积,我们遍历输入代表墙壁的字符串,如果与当前右墙壁对应的左墙壁的下标比该栈顶的水坑的key还要小,说明这是在一个水坑,需要面积增加或者合并水坑,大家可以看看下面这个示例:
左图是两个单独的小坑,以(2,5)和(7,4)的形式存放在栈中,但是当遍历到11的右墙壁时,找到与之对应的下标为1的左墙壁,这时候就要不断先后从栈中弹出水坑(7,4)和(2,5),因为其下标7,2都大于1,这时就将这两个小水坑和上一层的水层合并构成(1,19)的大水坑。
上面说了这么多,大问题也都解决了,还有个小问题,遇到"_"平坑怎么办?其实跳过就行了,因为"_"的作用就是令水坑的面积加以,这个在给墙壁定义下标时就已经把"_"的影响考虑进去了。
不说了,源码附上来:
#include<iostream>
#include<stack>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
stack<int> S1;
stack<pair<int ,int> > S2;
char ch;
int sum=0;
string str="";
cin>>str;
for(int i=0;i<str.size();i++)
{
ch=str[i];
if(ch=='\\') //注意这里不能写成'\'因为要对转义字符进行转义
{
S1.push(i);
}
else if(ch=='/' && S1.size()>0)
{
int j=S1.top();S1.pop(); //找到与右墙壁对应的左墙壁
sum+=i-j; //计算那一行的面积,并加到总面积中
int a=i-j; //计算那一行的面积
while(S2.size()>0 && S2.top().first>j) //S2中每个目前完整小坑的面积,也可能是多个小坑构成的不完整的大坑面积
{
a+=S2.top().second; //通过S2.top().first>j条件不断合并面积
S2.pop();
}
S2.push(make_pair(j,a));
}
}
vector<int> ans;
while(S2.size()>0)
{
ans.push_back(S2.top().second);S2.pop();
}
reverse(ans.begin(),ans.end());
cout<<sum<<endl;
cout<<ans.size();
for(int i=0;i<ans.size();i++)
{
cout<<" ";
cout<<ans[i];
}
cout<<endl;
}
总结:遇到在一连串带有穿插的匹配问题时要考虑到使用栈,像本题和表达式中括号的匹配问题等~
PS:开始慢慢接触到一些比较有难度的题目,希望坚持下来~之后附上的代码会有书上已经给出的方法,自己有另外的方法也会附上,大神不要嘲笑就好,哈哈~