题意 : 有一个数组,求这个数组的最长上升子序列有多少种;每个数字只能用一次,即一个数字只能算其中的一种;
思路:思路跟最短路径+最大流是一样的,用最大流去限定,求出最短路径的个数。同样,求出最长上升子序列,再用最大流去限定,求出的结果就是最长上升子序列的个数。求LCIS则用常规方法,既定义数组dp[i]是表示以 i 结尾的最长上升子序列的长度,求出最长序列ans。由于数列中的每一个数只能使用一次,构图的时候需要拆点。若有n个数,则拆成2 * n个点,构造源点和汇点,将每个点都和自己拆出来的点连边,将源点和最长序列为1的点连边,将最长序列为ans的点与汇点连边,最后若dp[j] = dp[i] + 1,则将i + n和 j连边。所有边的流量都是1,这样便可以限制每个点只使用一次。其实连边的顺序就是最长序列为1,2,3,...,ans。可以理解为从最长序列为1(该数本身)一直流到数列中的最长上升序列。画出图来就好理解了
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=8888 ;
const int inf = 0xfffffff ;
struct node
{
int u,v,next,c;
}edge[N*88] ;
int head[N],pre[N],gap[N],dis[N],d[N],cur[N] ,x[N];
int top ,s,t,vn,n ;
void add(int u ,int v,int c)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
int LCIS() //最长上升子序列
{
for(int i = 1 ; i <= n ;i++)
d[i]=1;
for(int i = 1 ; i <= n ; i++)
for(int j= 1 ;j < i ; j++)
{
if(x[i]>x[j] && d[j]+1 > d[i]) //d[j]+1 > d[i],才更新,
d[i] = d[j]+1 ;
}
int ans=0;
for(int i = 1 ; i <= n ; i++)
ans = max(ans,d[i]) ;
return ans ;
}
int max_flow()
{
memset(pre,-1,sizeof(pre));
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
int id,u=s,v,nect,cur_flow,mindis,Max_flow=0,i;
gap[0]=vn;
//pre[s]=s;
for( i = 0 ; i < vn ; i++) cur[i]=head[i] ;
while(dis[s] < vn)
{
if(u==t)
{
cur_flow=inf ;
for( i = s ; i != t ; i=edge[cur[i]].v)
{
if( edge[cur[i]].c < cur_flow)
{
cur_flow = edge[cur[i]].c;
nect = i ;
}
}
for( i = s ; i != t ; i=edge[cur[i]].v)
{
id =cur[i] ;
edge[id].c -=cur_flow ;
edge[id^1].c +=cur_flow ;
}
Max_flow += cur_flow ;
u=nect ;
}
for( i = cur[u] ; i!=-1 ;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].c>0 && dis[u]==dis[v]+1 )
break ;
}
if(i!=-1)
{
pre[v]=u;
cur[u]=i;
u=v;
}
else
{
if(--gap[dis[u]]==0) break;
cur[u]=head[u];
mindis=vn;
for( i = cur[u] ; i!=-1 ;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].c>0 && dis[v]<mindis)
{
mindis=dis[v];
cur[u]=i;
}
}
dis[u]=mindis+1;
gap[dis[u]]++;
if(u!=s) u=pre[u];
}
}
return Max_flow ;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i = 1 ; i <= n ;i++)
scanf("%d",&x[i]);
int ans = LCIS() ;
top=0;
memset(head,-1,sizeof(head));
s=0;t=2*n+1;vn=t+1;
for(int i = 1 ; i <= n ;i++)
{
add(i,i+n,1); //拆点,亮点连边
if(d[i]==1)
add(s,i,1); //与源点连
if(d[i]==ans) //只有长度为最长子序列,才与汇点相连,保证了,最后流到汇点的就是符合的个数;
add(i+n,t,1);
for(int j = i+1 ; j <= n ;j++ ) //j跟在i后面组成的序列,长度为d[j];
if(d[j]==d[i]+1)
add(i+n,j,1) ;
}
int num = max_flow() ; //跑一边最大流;
printf("%d\n%d\n",ans,num);
}
return 0;
}