独立集

独立集


题目描述 
有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。于是他就想把这两个东西结合在一起。众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。 
这个算法不标准的伪代码如下: 
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。 
//结束。 
那么我们要算出这个无向图G最大独立集的大小。但是事情不止于此。顺旺基同学有时候心情会不爽,这个时候他就会要求你再回答多一个问题:最大独立集可能不是唯一的,但有些点是一定要选的,问哪些点一定会在最大独立集里。今天恰好他不爽,被他问到的同学就求助于你了。 
输入 
两行。第一行为N,第二行为1到N的一个全排列。 
输出 
两行。第一行输出最大独立集的大小,第二行从小到大输出一定在最大独立集的点的编号(输入时的序号)。 
样例输入 

3 1 2 
样例输出 

2 3 
提示 
【数据范围】

30%的数据满足 N<=16

60%的数据满足 N<=1,000

100%的数据满足 N<=100,000 

没想到一场比赛还能有两道DP,晕过去了+_+… 
此题一大重点就是要从题目中发现是要求本身就在最长上升序列中不会被交换的数的个数和数。 
知道了这一点,我们用dp[i][0]表示以i结尾包括i的最长上升子序列长度,dp[i][1]表示以i为头包括i的最长上升子序列长度,用二分查找求得,做两次。O(nlogn)… 
个数已经求出,剩下就是求是哪些数了,这些数首先要满足dp[i][0]+dp[i][1]=ans+1这一点,即它并不用改动位置,其次还要有必要性,用一个计数器统计即可。时间复杂度O(nlogn).可以通过本题qwq。。。

代码块


#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std; 
int n,sum[200000],a[200000],f[200000],dp[200000][2]; 
int main() 
{ 
scanf(“%d”,&n); 
for(int i=1;i<=n;i++) scanf(“%d”,&a[i]); 
int t,num=0; 
for(int i=1;i<=n;i++) 
{ 
t=lower_bound(f,f+num+1,a[i])-f; 
if(t>num) num=t; 
dp[i][0]=t; 
f[t]=a[i]; 
} 
f[0]=-(1<<30-1); 
num=0; 
for(int i=n;i>0;i–) 
{ 
t=lower_bound(f,f+num+1,-a[i])-f; 
if(t>num) num=t; 
dp[i][1]=t; 
f[t]=-a[i]; 
} 
printf(“%d\n”,num); 
for(int i=1;i<=n;i++) 
if(dp[i][0]+dp[i][1]==num+1) sum[dp[i][0]]++; 
for(int i=1;i<=n;i++) 
if(dp[i][0]+dp[i][1]==num+1 && sum[dp[i][0]]==1) printf("%d ",i); 
return 0; 
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值