zoukankan      html  css  js  c++  java
  • luogu2766 最长不下降子序列问题 DP 网络流

    题目大意:给定正整数序列x1,...,xn 。(1)计算其最长不下降子序列的长度s。(不一定是否连续)(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。(序列内每一个元素不可重复)(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。

    关键词:不相交路径,拆点

    (1)DP经典题LIS。令原序列为A[i],DP[i]表示以i为结尾的不下降子序列长度最长为多少。

    (2)

    不相交路径:想象有一个流把一个子序列内的每一个元素都串了起来。后一个元素与前一个元素可连接的标志是DP[j]=DP[i]+1且A[i]<=A[j]。

    拆点:每个元素只能访问一次,因此把一个元素拆成一条容量为1的边,两元素若可连接,则将前元素的to点连后元素的from点。为了从头开始,从尾结束,因此将S与DP[i]=1的元素边的from点连,DP[i]=S的元素边的to点与T连。然后求最大流即可。

    (3)将与1元素边的from点和与n元素边的to节点相连的正向边容量设为∞,然后求最大流。

    #include <cstdio>
    #include <cstring>
    #include <cassert>
    #include <queue>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    
    #define LOOP(i,n) for(int i=1; i<=n; i++)
    const int MAX_NODE = 1010, MAX_EDGE = MAX_NODE*MAX_NODE, INF = 0x3f3f3f3f;//注意MAX_NODE=500*2
    int A[MAX_NODE], DP[MAX_NODE];
    int Tot, MaxLen, Sid, Tid;
    //#define test
    
    struct Dinic
    {
        struct Edge;
        struct Node;
    
        struct Node
        {
            int Id, Level;
            Edge* Head;
            Edge *DfsFrom;
        };
    
        struct Edge
        {
            int Cap, OrgCap;
            Node *From, *To;
            Edge *Next, *Rev;
            Edge(int cap, Node *from, Node *to, Edge *next) :Cap(cap), OrgCap(cap), From(from), To(to), Next(next) {}
        };
    
        Node _nodes[MAX_NODE];
        Edge *_edges[MAX_EDGE];
        int _vCount, _eCount;
        Node *Start, *Target;
    
        void Init(int Sid, int Tid, int vCount)
        {
            memset(_nodes, 0, sizeof(_nodes));
            memset(_edges, 0, sizeof(_edges));
            _eCount = 0;
            Start = Sid + _nodes;
            Target = Tid + _nodes;
            _vCount = vCount;
        }
    
        void Recover()
        {
            LOOP(i, _eCount)
                _edges[i]->Cap = _edges[i]->OrgCap;
        }
    
        Edge* AddEdge(Node *from, Node *to, int cap)
        {
            Edge *e = _edges[++_eCount] = new Edge(cap, from, to, from->Head);
            e->From->Head = e;
            return e;
        }
    
        void Build(int uId, int vId, int eCap)
        {
            Node *u = uId + _nodes, *v = vId + _nodes;
            u->Id = uId;
            v->Id = vId;
            Edge *edge1 = AddEdge(u, v, eCap), *edge2 = AddEdge(v, u, 0);
            edge1->Rev = edge2;
            edge2->Rev = edge1;
        }
    
        bool Bfs()
        {
            for (int i = 1; i <= _vCount; i++)
                _nodes[i].Level = 0;
            static queue<Node*> q;
            Start->Level = 1;
            q.push(Start);
            while (!q.empty())
            {
                Node *u = q.front();
                q.pop();
                for (Edge *e = u->Head; e; e = e->Next)
                {
                    assert(e->Cap >= 0);
                    if (!e->To->Level && e->Cap)
                    {
                        e->To->Level = u->Level + 1;
                        q.push(e->To);
                    }
                }
            }
            return Target->Level;
        }
    
        int Dfs(Node *cur, int limit)
        {
            if (cur == Target)
                return limit;
            if (limit == 0)
                return 0;
            int curTake = 0;
            for (Edge *e = cur->DfsFrom; e; cur->DfsFrom = e = e->Next)
            {
                if (e->To->Level == cur->Level + 1 && e->Cap)
                {
                    int nextTake = Dfs(e->To, min(limit - curTake, e->Cap));
                    e->Cap -= nextTake;
                    e->Rev->Cap += nextTake;
                    curTake += nextTake;
                }
                if (limit - curTake == 0)
                    break;
            }
            return curTake;
        }
    
        int Proceed()
        {
            int ans = 0;
            while (Bfs())
            {
                for (int i = 0; i <= _vCount; i++)
                    _nodes[i].DfsFrom = _nodes[i].Head;
                ans += Dfs(Start, INF);
            }
            return ans;
        }
    }g;
    
    void P1()
    {
        LOOP(j, Tot)
        {
            int maxLen = 0;
            LOOP(i, j - 1)
                if (A[i] <= A[j])
                    maxLen = max(maxLen, DP[i]);
            DP[j] = maxLen + 1;
        }
        MaxLen = 0;
        LOOP(i, Tot)
            MaxLen = max(MaxLen, DP[i]);
        printf("%d
    ", MaxLen);
    }
    
    void P2()
    {
        Sid = Tot * 2 + 1, Tid = Tot * 2 + 2;
        g.Init(Sid, Tid, Tid);
        LOOP(i, Tot)
            g.Build(i, i + Tot, 1);
        LOOP(i, Tot)
        {
            if (DP[i] == 1)
                g.Build(Sid, i, 1);
            if (DP[i] == MaxLen)
                g.Build(i + Tot, Tid, 1);
            else 
                for (int j = i + 1; j <= Tot; j++)
                    if (DP[j] == DP[i] + 1 && A[j] >= A[i])
                        g.Build(i + Tot, j, 1);
        }
        printf("%d
    ", g.Proceed());
    }
    
    void P3()
    {
        for (Dinic::Edge* e = g._nodes[1].Head; e; e = e->Next)
        {
            if (e->To->Id == 1 + Tot)
                e->OrgCap = INF;
            else if (e->To == g.Start)
                e->Rev->OrgCap = INF;
        }
        for (Dinic::Edge *e = g._nodes[Tot * 2].Head; e; e = e->Next)
        {
            if (e->To->Id == Tot)
                e->Rev->OrgCap = INF;
            else if (e->To == g.Target)
                e->OrgCap = INF;
        }
        g.Recover();
        printf("%d
    ", g.Proceed());
    }
    
    int main()
    {
    #ifdef _DEBUG
        freopen("c:\noi\source\input.txt", "r", stdin);
    #endif
        scanf("%d", &Tot);
        LOOP(i, Tot)
            scanf("%d", i + A);
        P1();
        P2();
        P3();
        return 0;
    }
    View Code

    注意:

    1.本题是“不下降”,而不是“上升”。因此(1)题DP时应该为A[i]<=A[j],而不是A[i]<A[j]。

    2.(2)问不要忘了判断元素可连接的标志还有A[i]<=A[j]。

    3.(3)题改容量时注意改的到底时正向边的容量还是反向边的容量,要清楚。

  • 相关阅读:
    bbs与blog的区别
    论坛的一个大弱点,就是知识的无序化,这一点有时阻碍了论坛的长久发展,造成了集体智慧的流失。
    中国百科
    网上的意识流整理,
    商业模式是怎么练成的?
    无线电的共享
    超细分众
    关于wifi社区
    图像话讨论区
    关于桌面软件的功能
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8453506.html
Copyright © 2011-2022 走看看