【网络流24题】最长不下降子序列问题_#include

Solution

Part 1

直接 dp 求解。如果用网络流也可以,但是会超时。

Part 2

设最长 LIS 长度为 \(Ans\)。

因为每个数只能使用一次,考虑拆成两个点 \(X_i\) 与 \(Y_i\),分别表示入点与出点,其中使 \(X_i\) 向 \(Y_i\) 连一条容量为 \(1\) 的边,表示这个数只能使用一次。

对于 \(i\),使 \(Y_i\) 向所有满足 \(j<i\) 并且 \(dp[j]=dp[i]-1\), \(A[j] \leq A[i]\) 的 \(X_j\) 连边。

若 \(dp[i] = Ans\),则从源点 \(S\) 向 \(X_i\) 连一条容量为 \(1\) 的边。若 \(dp[i] = 1\),则从 \(Y_i\) 向汇点 \(T\) 连一条容量为 \(1\) 的边。

跑一边最大流,得到的最大流量即为答案。

Part 3

对于 \(Ans=1\) 的情况特判,以下讨论 \(Ans >= 2\) 的情况。

在 Part 2 的基础上,将 \(< Y_1 , X_1 >\) 与 \(< Y_n , X_n >\) 的容量改为 \(inf\) ,表示可以无限取。

若存在 \(< S , X_n >\) ,\(< Y_1 , T >\) 这种边,也将他们的容量改为 \(inf\) ,因为它们都可以给出无限次贡献。

另外的,若 \(Ans = 2\) 且存在 \(< Y_n , X_1 >\) ,则将其删去,否则会造成贡献计算无限次的情况,最后再将其贡献加上。

跑一边最大流,得到的最大流量即为答案。

Code

#include<bits/stdc++.h>
#define N 1205
#define M 30005
using namespace std;
typedef long long ll;
typedef double db;
char IO;
int rd(){
	int num=0;bool f=0;
	while(IO=getchar(),IO<48||IO>57)if(IO=='-')f=1;
	do num=(num<<1)+(num<<3)+(IO^48);
	while(IO=getchar(),IO>=48&&IO<=57);
	return f?-num:num;
}
bool f2;
int n,S,T;
int to[M],nxt[M],hd[N],val[M],cnte=1;
void Adde(int u,int v,int w){
	to[++cnte]=v;val[cnte]=w;
	nxt[cnte]=hd[u];hd[u]=cnte;
}
void Add(int u,int v,int w){
	Adde(u,v,w);Adde(v,u,0);
}
int dep[M],cur[M];
queue<int> Q;
int BFS(){
	memset(dep,0,sizeof(dep));
	memcpy(cur,hd,sizeof(cur));
	dep[S]=1;Q.push(S);
	int u,v;
	while(!Q.empty()){
		u=Q.front();Q.pop();
		for(int i=hd[u];i;i=nxt[i]){
			if(val[i]&&!dep[v=to[i]]){
				dep[v]=dep[u]+1;
				Q.push(v);
			}
		}
	}
	return dep[T];
}
int DFS(int u,int flow){
	if(u==T)return flow;
	int res=0,f,v;
	for(int i=cur[u];i&&flow;i=nxt[i]){
		cur[u]=i;v=to[i];
		if(dep[v]!=dep[u]+1)continue;
		if(val[i]&&(f=DFS(v,min(flow,val[i])))>0){
			val[i]-=f;val[i^1]+=f;
			flow-=f;res+=f;
		}
	}return res;
}
int A[N],dp[N];
bool f1;
int main(){
//	cout<<1.0*(&f1-&f2)/1024.0/1024.0<<endl;
	n=rd();
	int Ans=0;
	for(int i=1;i<=n;++i){
		A[i]=rd();dp[i]=1;
		for(int j=1;j<i;++j)
			if(A[j]<=A[i])dp[i]=max(dp[i],dp[j]+1);
		Ans=max(Ans,dp[i]);
	}cout<<Ans<<endl;
	if(n==1){
		printf("1\n1");
		return 0;
	}
	S=0,T=2*n+1;
	for(int i=1;i<=n;++i){
		if(dp[i]==Ans)Add(S,i,1);
		if(dp[i]==1)Add(i+n,T,1);
		Add(i,i+n,1);
		for(int j=1;j<i;++j)
			if(A[j]<=A[i]&&dp[j]==dp[i]-1)Add(i+n,j,1);
	}
	int res=0;
	while(BFS())
		res+=DFS(S,1e9);
	cout<<res<<endl;
	cnte=1;memset(hd,0,sizeof(hd));
	for(int i=1;i<=n;++i){
		if(dp[i]==Ans){
			if(i!=n)Add(S,i,1);
			else Add(S,i,1e9);
		}
		if(dp[i]==1){
			if(i!=1)Add(i+n,T,1);
			else Add(i+n,T,1e9);
		}
		if(i==1||i==n)Add(i,i+n,1e9);
		else Add(i,i+n,1);
		for(int j=1;j<i;++j)
			if(A[j]<=A[i]&&dp[j]==dp[i]-1&&!(i==n&&j==1))Add(i+n,j,1);
	}
	res=0;
	while(BFS())
		res+=DFS(S,1e9);
	if(Ans==2&&A[1]<=A[n])++res;
	cout<<res;
	return 0;
}