6005-最长递增子序列 -DP-最大流

6 篇文章 0 订阅

 

  •  
  • n<=500
  • 思路:先用dp求出第一问的答案,和 dp 数组,dp[i]代表以 i为终点最长不下降子序列的长度
  • 对于第二问,源点T向 dp[i] 等于第一问答案的点连边,dp[i]=1 的点向汇点S连边,中间的点 u 和点 v ,如果 
  • dp[u]=dp[v]+1且 a[u]>=a[v] ,那么它们之间连边,这些边容量均为 1 。并且因为每个点只能用一次,所以拆点,
  • 中间连容量为 1的边。跑最大流就是第二问的答案,对于第三问,把一号点和最后一个点的拆点的容量与
  • 它们连向源点汇点的流量设为 inf 就好了
  • #include<bits/stdc++.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define maxn 11234
    int n,a[maxn],dp[maxn],s1,s2;
    int cnt,ans,s,t,head[maxn];
    int level[maxn];
    struct node
    {
        int v,to,w;
    } edge[255678];
    void add(int u,int v,int w)
    {
        edge[cnt].v=v;
        edge[cnt].to=head[u];
        edge[cnt].w=w;
        head[u]=cnt++;
        edge[cnt].v=u;
        edge[cnt].to=head[v];
        edge[cnt].w=0;
        head[v]=cnt++;
    }
    void caldp()
    {
        for(int i=1; i<=n; i++)
        {
            dp[i]=1;
            for(int j=i-1; j>=1; j--)
                if(a[i]>=a[j])
                    dp[i]=max(dp[i],dp[j]+1);
            ans=max(dp[i],ans);
        }
    }
    void bulid1()
    {
        memset(head,-1,sizeof(head));
        s=0,t=n*3;
        for(int i=1; i<=n; i++)
            add(i,i+n,1);
        for(int i=n; i>=1; i--)
        {
            if(dp[i]==ans)
                add(t,i,1);
            if(dp[i]==1)
                add(i+n,s,1);
            for(int j=i-1; j>=1; j--)
                if(dp[j]+1==dp[i]&&a[i]>=a[j])
                    add(i+n,j,1);
        }
    }
    void bulid2()
    {
        memset(head,-1,sizeof(head));
        s=0,t=n*3;
        for(int i=1; i<=n; i++)
        {
            if(i==n||i==1)
            {
                add(i,i+n,inf);
                if(dp[i]==ans)add(t,i,inf);
                if(dp[i]==1)add(i+n,s,inf);
            }
            else
            {
                add(i,i+n,1);
                if(dp[i]==ans)add(t,i,1);
                if(dp[i]==1)add(i+n,s,1);
            }
        }
        for(int i=n; i>=1; i--)
        {
            for(int j=i-1; j>=1; j--)
                if(dp[j]+1==dp[i]&&a[i]>=a[j])
                    add(i+n,j,1);
        }
    }
    bool dinic_bfs()
    {
        memset(level,0,sizeof(level));
        queue<int>que;
        que.push(t);
        level[t]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            for(int i=head[u]; i!=-1; i=edge[i].to)
            {
                int v=edge[i].v;
                if(!level[v]&&edge[i].w>0)
                {
                    level[v]=level[u]+1;
                    que.push(v);
                }
            }
        }
        return level[s]!=0;
    }
    int dinic_dfs(int u,int cpflow)
    {
        if(u==s)return cpflow;
        int addflow=0;
        for(int i=head[u]; i!=-1&&addflow<cpflow; i=edge[i].to)
        {
            int v=edge[i].v;
            if(level[u]+1==level[v]&&edge[i].w>0)
            {
                int temp=dinic_dfs(v,min(cpflow-addflow,edge[i].w));
                edge[i].w-=temp;
                edge[i^1].w+=temp;
                addflow+=temp;
            }
        }
        return addflow;
    }
    int dinic()
    {
        int maxflow=0;
        while(dinic_bfs())
            maxflow+=dinic_dfs(t,inf);
        return maxflow;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        caldp();
        bulid1();
        s1=dinic();
        if(ans==1)
            printf("1\n%d\n%d\n",s1,n);
        else
        {
            bulid2();
            s2=dinic();
            printf("%d\n%d\n%d\n",ans,s1,s2);
        }
        return 0;
    }
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值