题目大意
一排水果,1苹果,0桔子。连续相同水果叫一个块。
每一轮操作:
把当前每一个块的最左边水果拿出来,组成一个果篮输出。
拿走之后块会缩短;如果中间分隔的块被拿空,左右两个同种类块会合并。
重复直到所有水果拿完。
暴力思路
每一轮遍历 1~n:
- vis[i]标记水果是否已经被取走;
- x记录本轮取出的上一个水果的种类;
- 如果没被取走,并且和上一轮取出种类不一样,说明是新块的左端点,取走这个水果。
缺陷:最坏情况比如1 0 1 0 1 0 …交替,每一轮要完整扫一遍数组,总复杂度O(n^2),n=2e5 直接超时,只能拿 70 分。
开 `vis [] 标记是否拿走,每轮从左扫一遍找块的起点,70 分。n 到 2e5 会 TLE,O(n^2)。
暴力代码(70分)
#include <iostream>
using namespace std;
int n,cnt=0;
int a[200005];
bool vis[200005];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
while(cnt<n)
{
int x=-1;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&x!=a[i])
{
cnt++;
cout<<i<<" ";
vis[i]=true;
x=a[i];
}
}
cout<<endl;
}
return 0;
}
AC思路
我们不需要每次遍历全部水果,而是直接用队列维护所有的块。
预处理原始数组,拆成若干块,每个块用deque存块内水果编号。
例:样例输入:1 1 0 0 1 1 1 0 1 1 0 0
原始分块:[1,2], [3,4], [5,6,7], [8], [9,10], [11,12]
每一轮:
- 遍历所有现存块,取出每个块队首元素,加入本轮答案;块弹出队首。
- 输出本轮答案。
- 重建块列表:跳过已经空掉的块;相邻两个非空块,如果种类相同,执行合并。
- 更新块列表,循环直到块列表为空。
由于每个水果只会入队出队各一次,总复杂度O(n)。
AC代码
#include <bits/stdc++.h>
using namespace std;
int n;
int main()
{
int n;
cin>>n;
vector<int> t(n+1);
for(int i=1;i<=n;i++)
{
cin>>t[i];
}
vector<deque<int>> b;//块的集合
deque<int> cur;//每一个小块
cur.push_back(1);//编号
for(int i=2;i<=n;i++)
{
if(t[i]==t[i-1])
{
cur.push_back(i);
}
else{
b.push_back(move(cur));
cur.clear();
cur.push_back(i);
}
}
if(!cur.empty())
{
b.push_back(move(cur));
}
//模拟每一轮
while(!b.empty())
{
vector<int> best;
//取
for(auto &dq:b)
{
best.push_back(dq.front());
dq.pop_front();
}
//输出
for(int i=0;i<best.size();i++)
{
cout<<best[i]<<" ";
}
cout<<endl;
//删除空块,合并相同的块
vector<deque<int>> a;//新的块的集合
for(auto &dq:b)
{
if(dq.empty()) continue;
if(!a.empty()){
int lf=a.back().front();
int c=dq.front();
if(t[lf]==t[c])
{
a.back().insert(a.back().end(),dq.begin(),dq.end());
continue;
}
}
a.push_back(move(dq));//move避免拷贝
}
b=move(a);
}
return 0;
}


被折叠的 条评论
为什么被折叠?



