链接:https://codeforces.com/contest/1556/problem/C
题意:
原来的序列是一个括号序列,此时给出它的压缩序列,“ ( ( ( ( ) ( ( ) ) ) ( ” 对应的压缩序列是[4,1,2,3,1],问在给定压缩序列的前提下,求出合法子序列的个数。
题解:
PS:合理括号序列一定有 ‘(’和‘)’的数量相同且任意时候‘(’的数量一定比‘)’多;
因为输入是成对出现的所以我们可以拆开一对一对看,对于单独的每一对,合法的数量就是取决于两值较小的那个,再看对与对之间会出现什么情况,对与对之间可能会连接在一起构造出合法序列,()那就要模拟这个过程;
拿第二组案例来说
6 1 3 2 1 2 4
构造出来的序列为“()))(()(())))”,一共有三对,分别是[1,3],[2,1],[2,4];
对第一对处理,自身可以构造1,左边没有对,所以ans+=1;
对第二对处理,自身可以构造1,因为之前左括号的数量大于右括号的数量所以不能向左延伸,所以ans+=1;
对第三对处理,自身可以构造2,因为多了两个右括号,所以可能向左延伸,左边原本有一对所以ans先加一,而且左边那对有多余的1个左括号可以消耗一个右括号去匹配ans再加1,再看能不能继续向左延伸,发现加上后,右括号的数量比左括号多,所以这一对跳过;
总计ans=6;
具体模拟过程看代码;
代码:
有注释:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int num[10005];
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> num[i];
}
ll cnt = 0;
for (int i = 1; i < n; i += 2)
{
ll x = 0;
ll temp = -num[i];
for (int j = i - 1; j >= 0; j -= 2)
{
x += num[j + 1];//总共右括号数量
temp += num[j + 1];//当前对,多余的右括号数
if (num[j] > x)//如果当前左括号比所有的右括号都多,答案加自身的可以构造的,如果能连上再加1,并且因为已经断了所以不再向左延伸
{
cnt += (x - temp);
if (temp > 0)//有多余的说明可以连上,要算上连上后的1;
{
cnt++;
}
break;
}
if (num[j] >= temp)//如果当前左括号比当前的多余的右括号多,答案加自身的可以构造的,如果能连上再加1,向左延伸
{
cnt += (num[j] - temp);//消耗掉多余的以后,剩下的就是可以独立构造的
if (temp > 0)//有多余的说明可以连上,要算上连上后的1;
{
cnt++;
}
}
temp -= min(ll(num[j]), temp);//表示当前对亏几个右括号
x -= num[j];//消耗的右括号的数量相当于当前的左括号的数量,所有直接减去当前左括号的数量
}
}
cout << cnt << endl;
return 0;
}
无注释:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int num[10005];
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> num[i];
}
ll cnt = 0;
for (int i = 1; i < n; i += 2)
{
ll x = 0;
ll temp = -num[i];
for (int j = i - 1; j >= 0; j -= 2)
{
x += num[j + 1];
temp += num[j + 1];
if (num[j] > x)
{
cnt += (x - temp);
if (temp > 0)
{
cnt++;
}
break;
}
if (num[j] >= temp)
{
cnt += (num[j] - temp);
if (temp > 0)
{
cnt++;
}
}
temp -= min(ll(num[j]), temp);
x -= num[j];
}
}
cout << cnt << endl;
return 0;
}