codevs1906 最长递增子序列问题

题目描述 Description

给定正整数序列x1,..... , xn  。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。

输入描述 Input Description

第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1.....xn 。

输出描述 Output Description

第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。

样例输入 Sample Input

4
3 6 2 5

样例输出 Sample Output

2
2
3

不得不吐槽codevs上面网络流24题好多数据都是很蛋疼的要输出方案又不给spj……鬼知道你数据是用哪种方案啊

啊这题还算正常

第一问LIS随便求

第二问拆点完S向所有x1连流量1费用0的边,所有x2向T连流量1费用0的边,所有x1向x2连流量1费用1的边。因为要表示有没有取这个点

对于所有a[i]<a[j]且i<j的,i2向j1连流量1费用0的边

然后跑最大费用。每次增广搞完如果答案是第一问的最长长度,那么ans++。

第三问就是第二问的建图与1和n有关的边的流量全改inf就对了

#include<cstdio>
#include<iostream>
#include<cstring>
#define LL long long
#define inf 0x3ffffff
#define S 0
#define T 1001
using namespace std;
inline LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int n,ans,flow,cnt,tot;
int a[510];
int mn[510];
struct edge{int from,to,next,v,c;}e[500010];
int head[1010],dis[1010],q[1010],from[1010];
bool mrk[1010];
inline void ins(int u,int v,int w,int c)
{
	e[++cnt].to=v;
	e[cnt].v=w;
	e[cnt].c=c;
	e[cnt].from=u;
	e[cnt].next=head[u];
	head[u]=cnt;
}
inline void insert(int u,int v,int w,int c)
{
	ins(u,v,w,c);
	ins(v,u,0,-c);
}
inline int bsearch(int x)
{
	int l=1,r=ans,s=0;
	while(l<=r)
	{
		int mid=(l+r)>>1l;
		if (mn[mid]<=x){s=mid;l=mid+1;}
		else r=mid-1;
	}
	return s;
}
inline bool spfa()
{
	for (int i=1;i<=T;i++)dis[i]=inf;
	memset(mrk,0,sizeof(mrk));
	int t=0,w=1;
	dis[S]=0;q[0]=S;mrk[S]=1;
	while (t!=w)
	{
		int now=q[t++];if (t==1005)t=0;
		for (int i=head[now];i;i=e[i].next)
		if (e[i].v&&dis[now]+e[i].c<dis[e[i].to])
		{
			dis[e[i].to]=dis[now]+e[i].c;
			from[e[i].to]=i;
			if (!mrk[e[i].to])
			{
				mrk[e[i].to]=1;
				q[w++]=e[i].to;
				if (w==1005)w=0;
			}
		}
		mrk[now]=0;
	}
	if (dis[T]==inf)return 0;
	return 1;
}
inline void mcf()
{
	int x=inf;flow=0;
	for (int i=from[T];i;i=from[e[i].from])
		x=min(x,e[i].v);
	for (int i=from[T];i;i=from[e[i].from])
		{
			e[i].v-=x;
			e[i^1].v+=x;
			flow+=x*e[i].c;
		}
	if (flow==-ans)tot++;
}
int main()
{
	n=read();
	for (int i=1;i<=n;i++)a[i]=read();
	ans=1;mn[1]=a[1];
	for	(int i=2;i<=n;i++)
	{
		int fnd=bsearch(a[i]);
		if (fnd==ans)mn[++ans]=a[i];
		else mn[fnd+1]=a[i];
	}
	printf("%d\n",ans);
	cnt=1;
	for(int i=1;i<=n;i++)
	{
		insert(S,i,1,0);
		insert(i+n,T,1,0);
		insert(i,i+n,1,-1);
	}
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (a[i]<=a[j])insert(i+n,j,1,0);
	while (spfa())mcf();
	printf("%d\n",tot);
	memset(head,0,sizeof(head));
	cnt=1;tot=0;
	insert(S,1,inf,0);insert(S,n,inf,0);
	insert(1+n,T,inf,0);insert(2*n,T,inf,0);
	insert(1,1+n,inf,-1);insert(n,2*n,inf,-1);
	for(int i=1;i<=n;i++)
	{
		insert(S,i,1,0);
		insert(i+n,T,1,0);
		insert(i,i+n,1,-1);
	}
	for (int i=1;i<n;i++)
		for (int j=i+1;j<=n;j++)
			if (a[i]<=a[j])
				if (i!=1&&j!=n)insert(i+n,j,1,0);
				else insert(i+n,j,inf,0);
	while (spfa())mcf();
	printf("%d\n",tot);
}

 

时间: 2024-10-10 17:18:52

codevs1906 最长递增子序列问题的相关文章

HDU 3998 Sequence (最长递增子序列+最大流SAP,拆点法)经典

Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1666    Accepted Submission(s): 614 Problem Description There is a sequence X (i.e. x[1], x[2], ..., x[n]). We define increasing subsequ

算法面试题 之 最长递增子序列 LIS

找出最长递增序列 O(NlogN)(不一定连续!) 参考 http://www.felix021.com/blog/read.php?1587%E5%8F%AF%E6%98%AF%E8%BF%9E%E6%95%B0%E7%BB%84%E9%83%BD%E6%B2%A1%E7%BB%99%E5%87%BA%E6%9D%A5 我就是理解了一下他的分析 用更通俗易懂的话来说说题目是这样 d[1..9] = 2 1 5 3 6 4 8 9 7 要求找到最长的递增子序列首先用一个数组b[] 依次的将d里面

[网络流24题]最长递增子序列问题

题目大意:给定长度为n的序列a,求:1.最长递增子序列长度:2.最多选出几个不相交的最长递增子序列:3.最多选出几种在除了第1个和第n个以外的地方不相交的最长递增子序列.(n<=1000) 思路:先倒着DP,求出f[i]表示以a[i]开头的最长的递增子序列长度,然后建图,若f[i]=最长递增子序列长度则S向i连1,若f[i]=1则i向T连1,若i<j且a[i]<a[j]且f[i]=f[j]+1则i向j连1,为保证每个点只被流一次,拆成入点和出点,流量限制1,跑最大流即可解决第二问,点1和

最大子数组之和、最大子数组之积、最长递增子序列求法

昨天做爱奇艺笔试题,最后一道编程题是求整型数组最长递增子序列,由于时间关系,没有完全写出来,今天重新来做做这一系列题. <1> 最大子数组之和 首先从最简单的最大子数组之和求取.数组里有正数.负数.零.设包含第 i 个元素的子数组的和为 Sum,则Sum的值为 Sum(i) = Sum(i-1) + arrey[i]; 显然如果arrey[i]<=0,则Sum(i)<=Sum(i-1);则必须把Sum(i)=arrey[i];同时maxSum用来保存Sum最大值.时间复杂度为o(n

求数组中最长递增子序列

编程之美有一道关于数组中最长递增子序列,题目如下: 写一个时间复杂度尽可能低的程序,求一个一维数组(N个元素)中最长递增子序列的长度. 例如在序列1,-1,2,-3,4,-5,6,-7中,其最长的递增子序列的长度为4(如1,2,4,6),从该书给的例子我们可以知道的是其最长的递增子序列可以不连续的. 作者利用动态规划方法给了三种解法. 解法一: 根据无后效性的定义,各阶段按照一定的次序排列好之后,对于某个给定阶段的状态来说,它以前各阶段的状态无法直接影响它未来的决策,而只能间接地通过当前状态来影

LIS 最长递增子序列问题

一,    最长递增子序列问题的描述 设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm.求最大的m值. 比如int* inp = {9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}: 二,解决: 1.用一个临时数组tmp保存这样一种状态:tmp[i]表示以i为终点的递增序列的长度: 比如inp =

hdu1087最长递增子序列

原题地址 简单dp题,LIS.不同之处是这里要求得的不是最长的子序列,而是权重和最长的子序列.其实大同小异. 状态数组就是到达每个位置的最大权重. LIS问题常用解法就是两个: 人人为我 我为人人 本题我用了我为人人的思路 .就是确定子序列起点,把其后面每一个大于它的值的位置的状态数组更新. #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using name

最长递增子序列 LIS 时间复杂度O(nlogn)的Java实现

关于最长递增子序列时间复杂度O(n^2)的实现方法在博客http://blog.csdn.net/iniegang/article/details/47379873(最长递增子序列 Java实现)中已经做了实现,但是这种方法时间复杂度太高,查阅相关资料后我发现有人提出的算法可以将时间复杂度降低为O(nlogn),这种算法的核心思想就是替换(二分法替换),以下为我对这中算法的理解: 假设随机生成的一个具有10个元素的数组arrayIn[1-10]如[2, 3, 3, 4, 7, 3, 1, 6,

【网络流24题】最长递增子序列

Description 给定正整数序列x1,..., xn. (1)计算其最长递增子序列的长度s. (2)计算从给定的序列中最多可取出多少个长度为s的递增子序列. (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列. 设计有效算法完成(1)(2)(3)提出的计算任务 Input 第1 行有1个正整数n(n<=500),表示给定序列的长度. 接下来的1 行有n个正整数x1,..., xn. Output 第1 行是最长递增子序列的长度s. 第2行是可