P2766 最长不下降子序列问题
Posted olinr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2766 最长不下降子序列问题相关的知识,希望对你有一定的参考价值。
(color{#0066ff}{题目描述})
?问题描述:
给定正整数序列x1,...,xn 。
(1)计算其最长不下降子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。
?编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
(color{#0066ff}{输入格式})
第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。
(color{#0066ff}{输出格式})
第1 行是最长不下降子序列的长度s。第2行是可取出的长度为s 的不下降子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的不下降子序列个数。
(color{#0066ff}{输入样例})
4
3 6 2 5
(color{#0066ff}{输出样例})
2
2
3
(color{#0066ff}{数据范围与提示})
(nleq 500)
(color{#0066ff}{题解})
第一问大水题(O(n^2)),LIS,暴力就行
第二问第三问要用网络流
拆点,序列的每个点拆成<x,y>
因为我们要找最多的序列,每个长度为ans
第一问求出了f[i]为以i结尾的最长不下降子序列的长度
现在对于每个i
如果f[i]==1,则原点向(i_x)连容量为 1 的边
如果f[i]==ans,则(i_y)向汇点连容量为 1 的边
对于每一个(i<j),如果(a[i] leq a[j]),并且(f[j]==f[i]+1),那么就从(i_y)向(j_x)连一条容量为1的边
对于每个i,连一条(i_x)到$i_y&的边
这样就保证了流过去一个,就是一个合法的序列,而且长度恰=ans,不会重复
对于第三问,因为(a_1,a_n)无限使用,把1和n的一些边变成inf就行了,在原基础上再跑一遍就行了
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#define _ 0
#define LL long long
inline LL in()
{
LL x=0,f=1; char ch;
while(!isdigit(ch=getchar()))(ch=='-')&&(f=-f);
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int inf=0x7fffffff;
struct node
{
int to,dis;
node *nxt,*rev;
node(int to=0,int dis=0,node *nxt=NULL):to(to),dis(dis),nxt(nxt){}
void *operator new (size_t)
{
static node *S=NULL,*T=NULL;
return (S==T&&(T=(S=new node[1024])+1024)),S++;
}
};
typedef node* nod;
nod head[10500],cur[10500];
int dep[10500];
int n,s,t;
std::queue<int> q;
int a[10500];
inline void add(int from,int to,int dis)
{
nod o=new node(to,dis,head[from]);
head[from]=o;
}
inline void link(int from,int to,int dis)
{
add(from,to,dis);
add(to,from,0);
head[from]->rev=head[to];
head[to]->rev=head[from];
}
inline bool bfs()
{
for(int i=s;i<=t;i++) dep[i]=0,cur[i]=head[i];
dep[s]=1;
q.push(s);
while(!q.empty())
{
int tp=q.front(); q.pop();
for(nod i=head[tp];i;i=i->nxt)
if(!dep[i->to]&&i->dis>0)
{
dep[i->to]=dep[tp]+1;
q.push(i->to);
}
}
return dep[t];
}
inline int dfs(int x,int change)
{
if(x==t||!change) return change;
int flow=0,ls;
for(nod i=cur[x];i;i=i->nxt)
{
cur[x]=i;
if(dep[i->to]==dep[x]+1&&(ls=dfs(i->to,std::min(change,i->dis))))
{
change-=ls;
flow+=ls;
i->dis-=ls;
i->rev->dis+=ls;
if(!change) break;
}
}
return flow;
}
inline int dinic()
{
int flow=0;
while(bfs()) flow+=dfs(s,inf);
return flow;
}
namespace partone
{
int f[10505],ans;
void main()
{
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
if(a[j]<=a[i]) f[i]=std::max(f[i],f[j]+1);
ans=std::max(ans,f[i]);
}
printf("%d
",ans);
}
}
namespace parttwo
{
int fuc;
using namespace partone;
void main()
{
for(int i=1;i<=n;i++)
{
link(i,i+n,1);
if(f[i]==1) link(s,i,1);
if(f[i]==ans) link(i+n,t,1);
}
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
if(a[j]<=a[i]&&f[i]==f[j]+1) link(j+n,i,1);
printf("%d
",fuc=dinic());
}
}
namespace partthree
{
using namespace partone;
void main()
{
link(1,1+n,inf),link(s,1,inf);
if(f[n]==ans) link(n+n,t,inf),link(n,n+n,inf);
printf("%d
",parttwo::fuc+dinic());
}
}
int main()
{
n=in();
s=1,t=(n<<1)+1;
for(int i=1;i<=n;i++) a[i]=in();
partone::main();
parttwo::main();
partthree::main();
return 0;
}
以上是关于P2766 最长不下降子序列问题的主要内容,如果未能解决你的问题,请参考以下文章