解题报告-序列(alis.*)
序列(alis.*)
题目描述
现在 yxr 给你一个数列,求出此数列的最长递增子序列(不一定连续)的长度。
如果问题就那么简单就好了。哈哈。
现在这个问题还有第 \(2\) 问,设此长度为 \(K\),求此数列可以同时取出多少个长度为K的递增子序列(每个数最多出现在一个子序列中)。
输入描述
第一行:一个整数 \(N\),表示数列长度。
第二行:有 \(N\) 个整数,表示这个数列。
输出描述
第一行:一个整数 \(K\),表示最长递增子序列的长度。
第二行:一个整数,表示最多可以同时取出多少个长度为 \(K\) 的递增子序列。
输入输出样例 #1
输入样例 #1
6
1 1 2 3 4 6
输出样例 #1
5
1
说明/提示
样例#1解释
\(K\) 明显是 \(5\)。
然后可以同时取出的子序列只有 (\(1,2,3,4,6\))。
数据范围
- 对于 \(40\)% 的数据:\(1 \leq N \leq 30\)。
- 对于 \(100\)% 的数据:\(1 \leq N \leq 1000\)。
U613779 序列 - 洛谷解题报告
网络流建模题。
第一问就不说了,就是求 LIS 而已。我用的是树状数组优化 DP。
重点是第二问。
显然,由于同一个数不能被多次使用,相当于一个最小性限制。于是我们可以用最大流处理。
设我们已经计算出动规数组 DP,其中 \(dp[i]\) 表示以位置 \(i\) 结尾的 LIS 长度,最长的 LIS 的长度为 \(len\)。
具体的,我们进行以下操作:
- 将每个点 \(i\) 拆成两个点 \(i_{in}\) 和 \(i_{out}\),连一条 \(i_{in} \rightarrow i_{out}\) 的边,容量为 \(1\),表示每个数只能用一次。
- 建立总汇点 \(t\),对于所有满足 \(dp[i]=len\) 的位置 \(i\),连一条 \(i_{out} \rightarrow t\) 的边,容量为 \(+\infin\)。
- 建立总源点 \(s\),对于所有满足 \(dp[i]=1\) 的位置 \(i\),连一条 \(s \rightarrow i_{in}\) 的边,容量为 \(+\infin\)。
- 对于 \(\forall 1\leq i \leq j \leq N\),如果有 \(dp[i]+1==dp[j]\) 且在原数列 \(X\) 中 \(x[i]<x[j]\),连一条 \(i_{out} \rightarrow j_{in}\) 的边,容量为 \(+\infin\)。这表示 \(dp[j]\) 可从 \(dp[i]\) 继承而来。
这就完事了,不过一开始的测试数据是有问题的……
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=101100;
#define ckmax(x,y) ( x=max(x,y) )
#define ckmin(x,y) ( x=min(x,y) )
inline int read()
{
int f=1,x=0; char ch=getchar();
while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar(); }
return f*x;
}
int n,w[N];
int len,ans;
int dp[N],U[N];
int NUM[N],top;
#define lowbit(x) (x&(-x))
int T[N];
void update(int x,int val)
{
for(x;x<=n;x+=lowbit(x))
ckmax(T[x],val);
}
int query(int x)
{
int sum=0;
for(;x>0;x-=lowbit(x))
ckmax(sum,T[x]);
return sum;
}
struct edge
{ int next,to,val; };
int head[N],cur[N],tot;
int s,t,lv[N];
edge e[N<<1];
inline void add_edge(int from,int to,int val)
{
e[tot].next=head[from];
e[tot].to=to;
e[tot].val=val;
head[from]=tot++;
}
inline void DoubleEdge(int u,int v,int w)
{
add_edge(u,v,w);
add_edge(v,u,0);
}
inline bool level(int s,int t)
{
memset(lv,0,sizeof(lv)); lv[s]=1;
queue<int> q; q.push(s);
while(!q.empty())
{
int u=q.front(); q.pop();
if(u==t) return true;
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if(e[i].val<1||lv[v])
continue;
lv[v]=lv[u]+1;
q.push(v);
}
}
return false;
}
int FindFlow(int u,int flow)
{
if(u==t) return flow;
else
{
int F=0;
for(int &i=cur[u];~i&&F<flow;i=e[i].next)
{
int v=e[i].to;
if(e[i].val<1||lv[v]!=lv[u]+1)
continue;
int w=FindFlow(v,min(e[i].val,flow-F));
F+=w;
e[i].val-=w;
e[i^1].val+=w;
}
return F;
}
}
inline int Dinic(int s,int t)
{
int x,ans=0;
while(level(s,t))
{
memcpy(cur,head,sizeof(head));
while((x=FindFlow(s,INF)))
ans+=x;
}
return ans;
}
signed main()
{
freopen("alis.in","r",stdin);
freopen("alis.out","w",stdout);
memset(head,-1,sizeof(head));
n=read();
for(int i=1;i<=n;i++)
NUM[++top]=(w[i]=read());
sort(NUM+1,NUM+top+1);
top=unique(NUM+1,NUM+top+1)-NUM-1;
for(int i=1;i<=n;i++)
w[i]=lower_bound(NUM+1,NUM+top+1,w[i])-NUM;
for(int i=1;i<=n;i++)
{
dp[i]=query(w[i]-1)+1;
update(w[i],dp[i]);
}
for(int i=1;i<=n;i++) ckmax(len,dp[i]);
s=0,t=n<<2;
for(int i=1;i<=n;i++) DoubleEdge(i,i+n,1);
for(int i=1;i<=n;i++) if(dp[i]==1) DoubleEdge(s,i,INF);
for(int i=1;i<=n;i++) if(dp[i]==len) DoubleEdge(i+n,t,INF);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(dp[i]+1==dp[j] && w[i]<w[j])
DoubleEdge(i+n,j,INF);
ans=Dinic(s,t);
printf("%d\n%d",len,ans);
return 0;
}