版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/83152042
题目链接<https://loj.ac/problem/6005>
题意:
给定正整数序列 x1∼xn ,以下递增子序列均为非严格递增。
- 计算其最长递增子序列的长度s。
- 计算从给定的序列中最多可取出多少个长度为 s 的递增子序列。
- 如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列。
题解:
- 第一问直接秒掉,写成upperbound即可。
- 第二问基于第一问的dp数组,源点与dp为1的连一条权值为1的边,dp为s的与汇点连一条权值为1的边。对于每一个点i,与它后面的点j且a[i]<=a[j]&&dp[j]==dp[i]+1连接一条权值为1的边。这样跑网络流就相当于模拟了。因为只能每个点只能取一次,所以还要拆点(其实不拆点也能水过)。
- 第三问就把两个点的容量限制变为无穷就行了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+7;
const int inf=1e9+7;
struct Edge{
int v,w,nxt;
Edge(int v=0,int w=0,int nxt=0):v(v),w(w),nxt(nxt){}
}e[N*30];
int edn,sp,tp;
int p[N],d[N],c[N];
void add(int u,int v,int w){
e[++edn]=Edge(v,w,p[u]);p[u]=edn;
e[++edn]=Edge(u,0,p[v]);p[v]=edn;
}
bool bfs(){
memset(d,-1,sizeof(d));d[sp]=0;
queue<int>q;q.push(sp);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=p[u];~i;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].w){
d[v]=d[u]+1;
q.push(v);
if(v==tp) return true;
}
}
}
return ~d[tp];
}
int dfs(int u,int b){
if(u==tp) return b;
int r=0;
for(int i=c[u];~i;i=e[i].nxt){
int v=e[i].v;
if(e[i].w&&d[v]==d[u]+1){
int x=min(e[i].w,b-r);
c[u]=i;
x=dfs(v,x);
r+=x;
e[i].w-=x;
e[i^1].w+=x;
if(r==b) break;
}
}
if(!r) d[u]=-2;
return r;
}
int dinic(){
int tot=0,t;
while(bfs()){
memcpy(c,p,sizeof(p));
while(t=dfs(sp,inf))
tot+=t;
}
return tot;
}
int a[N],g[N],dp[N],n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
g[i]=inf;
}
g[n]=inf;
for(int i=0;i<n;i++){
int k=upper_bound(g+1,g+1+n,a[i])-g;
dp[i]=k;
g[k]=a[i];
}
int ans=0;
for(int i=0;i<n;i++)
ans=max(ans,dp[i]);
printf("%d\n",ans);
memset(p,-1,sizeof(p));edn=-1;
sp=2*n+1,tp=sp+1;
for(int i=0;i<n;i++){
if(dp[i]==1) add(sp,i,1);
if(dp[i]==ans) add(i+n,tp,1);
for(int j=i+1;j<n;j++){
if(a[i]<=a[j]&&dp[j]==dp[i]+1)
add(i+n,j,1);
}
add(i,i+n,1);
}
printf("%d\n",dinic());
memset(p,-1,sizeof(p));edn=-1;
sp=n*2+1,tp=sp+1;
for(int i=0;i<n;i++){
if(dp[i]==1){
if(i==0) add(sp,i,inf);
else add(sp,i,1);
}
if(dp[i]==ans){
if(i==n-1) add(i,tp,inf);
else add(i+n,tp,1);
}
for(int j=i+1;j<n;j++){
if(a[i]<=a[j]&&dp[j]==dp[i]+1)
add(i+n,j,1);
}
if(i==0||i==n-1) add(i,i+n,inf);
else add(i,i+n,1);
}
printf("%d\n",dinic());
}