【问题描述】
有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。于是他就想把这两个东西结合在一起。众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。
这个算法不标准的伪代码如下:
Pascal版本
procedure bubblesortgraph(n, a[]) :
//输入:点数n,1到n的全排列a。
//输出:一个点数为n的无向图G。
//创建一个有n个点,0条边的无向图G。
repeat
swapped = false
for i 从 1 到 n-1 :
if a[i] > a[i + 1] :
//在G中连接点a[i]和点a[i + 1]
//交换a[i]和a[i + 1]
swapped = true
until not swapped
//输出图G。
//结束。
C/C++版本
void bubblesortgraph(n,a[])
//输入:点数n,1到n的全排列a
//输出:一个点数为n的无向图G
{ //创建一个有n个点,0条边的无向图G。
do{ swapped=false
for i 从1 到n-1
if(a[i]>a[i+1])
{ //在G中连接点a[i]和点a[i+1]
//交换a[i]和a[i+1]
swapped =true
}
}while(swapped);
//输出图G。
}
//结束
那么我们要算出这个无向图G最大独立集的大小。但是事情不止于此。顺旺基同学有时候心情会不爽,这个时候他就会要求你再回答多一个问题:最大独立集可能不是唯一的,但有些点是一定要选的,问哪些点一定会在最大独立集里。今天恰好他不爽,被他问到的同学就求助于你了。
【输入格式】
输入两行。第一行为N,第二行为1到N的一个全排列。
【输出格式】
输出两行。第一行输出最大独立集的大小,第二行从小到大输出一定在最大独立集的点的编号。
【输入输出样例】
bubble.in bubble.out
3
3 1 2 2
2 3
【数据范围】
30%的数据满足 N<=16。
60%的数据满足 N<=1,000。
100%的数据满足 N<=100,000。
【分析】 O(nlogn) 最长不下降(上升)子序列
- 首先得看懂题目:我们要求独立集上的点互相之间没有边,则首先要满足 a[i]≤a[i+1] ,又因为这是冒泡排序,实际上独立集中的点就满足 a[j]≤a[i](j<i) ,那么最大独立集就被我们转化为求最长不下降子序列,直接用 O(nlogn) 算法求出,记为 Ans
- 然后我们考虑第二问:记
f1[i]
表示区间
[1,i]
中包含点
i
的最长不下降子序列长度,
f2[i] 表示区间 [i,n] 中包含点 i 的最长不下降子序列长度(也就是从点n 开始,倒着求一遍最长不上升子序列),那么对于可能存在于区间 [1,n] 中的最长不下降子序列的点,必然要满足 f1[i]+f2[i]−1=Ans ,我们将这些点统计出来 - 接下来我们记
G[k]
表示长度为
k
的最长不下降子序列最多能以多少个不同的点结尾,令可能存在于最长不下降子序列中的点
i 的 G[f1[i]]++ 。那么若对这些点统计完后仍存在 G[f1[i]]=1 ,则这个点 i 一定在最长不下降子序列中,也就是我们第二问的答案 - 最后顺便附上
O(nlogn) 求最长不下降子序列算法讲解链接:
http://blog.163.com/general_happy/blog/static/1693514082011024112934126/
【代码】
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 0x3f3f3f3f;
const int N = 1e5 + 5;
int f1[N], f2[N], G[N], a[N], b[N];
int n, k, Ans;
inline int get()
{
char ch; int res;
while ((ch = getchar()) < '0' || ch > '9');
res = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
res = (res << 3) + (res << 1) + ch - '0';
return res;
}
inline void put(int x)
{
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}
inline int Find1(const int &x)
{
int l = 1, r = k, res = 0;
while (l <= r)
{
int mid = l + r >> 1;
if (b[mid] < x) res = mid, l = mid + 1;
else r = mid - 1;
}
return res + 1;
}
inline int Find2(const int &x)
{
int l = 1, r = k, res = 0;
while (l <= r)
{
int mid = l + r >> 1;
if (b[mid] > x) res = mid, l = mid + 1;
else r = mid - 1;
}
return res + 1;
}
int main()
{
freopen("bubble.in", "r", stdin);
freopen("bubble.out", "w", stdout);
n = get(); int x;
for (int i = 1; i <= n; ++i) a[i] = get();
for (int i = 1; i <= n; ++i)
if (a[i] >= b[k]) b[++k] = a[i], f1[i] = k;
else f1[i] = x = Find1(a[i]), b[x] = a[i];
Ans = k; b[k = 0] = Maxn;
for (int i = n; i >= 1; --i)
if (a[i] <= b[k]) b[++k] = a[i], f2[i] = k;
else f2[i] = x = Find2(a[i]), b[x] = a[i];
put(Ans), putchar('\n');
for (int i = 1; i <= n; ++i)
if (f1[i] + f2[i] - 1 == Ans) G[f1[i]]++;
for (int i = 1; i <= n; ++i)
if (f1[i] + f2[i] - 1 == Ans && G[f1[i]] == 1) put(i), putchar(' ');
fclose(stdin); fclose(stdout);
return 0;
}