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\n", 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\n", 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\n", 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)题改容量时注意改的到底时正向边的容量还是反向边的容量,要清楚。

 

posted @ 2018-02-19 09:13  headboy2002  阅读(125)  评论(0编辑  收藏  举报