[日常训练] 独立集

来源:互联网 发布:淘宝增值服务是什么 编辑:程序博客网 时间:2024/05/18 01:26

【问题描述】

有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。于是他就想把这两个东西结合在一起。众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。
这个算法不标准的伪代码如下:
Pascal版本

procedure bubblesortgraph(n, a[]) ://输入:点数n,1到n的全排列a。//输出:一个点数为n的无向图G。//创建一个有n个点,0条边的无向图G。repeat   swapped = false   for i 从 1 到 n-1if a[i] > a[i + 1] :          //在G中连接点a[i]和点a[i + 1]          //交换a[i]和a[i + 1]          swapped = trueuntil 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的最长不下降子序列最多能以多少个不同的点结尾,令可能存在于最长不下降子序列中的点iG[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;}
原创粉丝点击