[日常训练] 独立集
来源:互联网 发布:淘宝增值服务是什么 编辑:程序博客网 时间: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-1 : if 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 的最长不下降子序列最多能以多少个不同的点结尾,令可能存在于最长不下降子序列中的点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;}
阅读全文
2 0
- [日常训练] 独立集
- HEU日常训练10.02
- 日常训练小结
- 日常训练20161012 道路网
- 日常训练20161012 醉酒
- 日常训练20161014 跟踪
- 日常训练20161018 证据
- 日常训练20161018 subset
- 日常训练 平均数
- 日常训练 水箱
- 日常训练 棋盘游走
- 日常训练 20170531 数字
- 日常训练 20170531 探险
- 日常训练 20170531 矩阵
- 日常训练 20170602 Book
- 日常训练 20170602 Equation
- 日常训练 20170603 棋盘
- 日常训练 20170605 EasyProblem
- 前端开发ajax请求失败或错误提示的解决办法
- HDU 6040 Hints of sd0061(nth_element)
- 对象数组排序&&对象数组去除重复数据
- (9)从站会开始
- 类的转换
- [日常训练] 独立集
- POJ
- BSGS&扩展BSGS
- 2017多校2 1004 Puzzle
- java锁学习笔记
- bootstrap轮播插件
- 字典的并集
- hdu1102 最小生成树
- 【学习笔记】熟悉并使用TypeScript的命名空间namespace