hdu 3998 (最长上升子序列+最大流)

 

题意 : 有一个数组,求这个数组的最长上升子序列有多少种;每个数字只能用一次,即一个数字只能算其中的一种;

思路:思路跟最短路径+最大流是一样的,用最大流去限定,求出最短路径的个数。同样,求出最长上升子序列,再用最大流去限定,求出的结果就是最长上升子序列的个数。求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;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值