HDU 3998 Sequence 最大流 isap 加 dp

22 篇文章 0 订阅
6 篇文章 0 订阅
  这道题目的意思是给你一个序列,要你求最长上升子序列,及最多有多少个最长上升子序列(一个数字用过了就不能再用).对于最长上升子序列的dp我就不多说了。下面是对最大流的建图。对于dp[i] = 1的点来说,建立源点到i的边,流量为1,对于dp[i] = 最长长度的时候,建立i到汇点的边,流量为1,对于其他的如果dp[i] + 1 = dp[j]&&num[j] > num[i],那么我们建立i到j的边,流量为1,最后跑一个最大流就好了。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define maxn 2000
#define qq 10000
#define maxm 200005
#define inf (1e8 + 7)
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;

int nums[2005], num[2005], heads[2005], sizes, d[2005], dp[2005], que[2005], n, ans1, ans2, source, sink, nn;
bool vis[2005];

struct edge
{
    int u, v, w, next;
}eg[8000000];

void inits()
{
    mem(num, 0);
    mem(vis, 0);
    mem(dp, 0);
    mem(heads, -1);
    mem(d, 0);
    sizes = 0;
    nn = 0;
    ans1 = 1;
    return;
}

void add(int u, int v)
{
    eg[sizes].v = v;
    eg[sizes].w = 1;
    eg[sizes].next = heads[u];
    heads[u] = sizes++;
    eg[sizes].v = u;
    eg[sizes].w = 0;
    eg[sizes].next = heads[v];
    heads[v] = sizes++;
    return;
}

void bfs()
{
    int head = 0, ta = 0;
    mem(vis, 0);
    d[sink] = 0;
    num[0] = 1;
    vis[sink] = 1;
    que[head++] = sink;
    while(ta != head)
    {
        int u = que[ta++];
        for(int i = heads[u];i != -1;i = eg[i].next)
        {
            int v = eg[i].v;
            if(!vis[v])
            {
                vis[v] = 1;
                d[v] = d[u] + 1;
                num[d[v]]++;
                que[head++] = v;
            }
        }
    }
    return;
}

int dfs(int a, int cos)
{
    if(a == sink)
        return cos;
    int dd, mins = nn + 1, lv = cos;
    for(int i = heads[a];i != -1;i = eg[i].next)
    {
        int v = eg[i].v;
        int w = eg[i].w;
        if(w)
        {
            if(d[v] + 1 == d[a])
            {
                dd = min(lv, w);
                dd = dfs(v, dd);
                eg[i].w -= dd;
                eg[i^1].w += dd;
                lv -= dd;
                if(d[source] >= nn + 2)
                    return cos - lv;
                if(!lv)
                    break;
            }
            mins = min(mins, d[v]);
        }
    }
    if(lv == cos)
    {
        num[d[a]]--;
        if(!num[d[a]])
            d[source] = nn + 2;
        d[a] = mins + 1;
        num[d[a]]++;
    }
    return cos - lv;
}

int isap()
{
    int st = source, ff = 0;
    while(d[st] < nn + 2)
        ff += dfs(st, inf);
    return ff;
}

int main()
{
    source = 2003;
    sink = 2004;
    int a;
    while(~scanf("%d", &n))
    {
        inits();
        for(int i = 0;i < n;i++)
            scanf("%d", &num[i]);
        dp[0] = 1;
        for(int i = 1;i < n;i++)
        {
            bool flag = 0;
            for(int j = i - 1;j >= 0;j--)
            {
                if(num[i] > num[j])
                    dp[i] = max(dp[i], dp[j] + 1), flag = 1;
            }
            if(!flag)
                dp[i] = 1;
            ans1 = max(ans1, dp[i]);
        }
        add(source, 0);
        if(ans1 == 1)
            add(0, sink);
        nn++;
        vis[0] = 1;
        for(int i = 1;i < n;i++)
        {
            if(dp[i] == 1)
            {
                if(!vis[i])
                    vis[i] = 1, nn++;
                add(source, i);
            }
            if(dp[i] == ans1)
            {
                if(!vis[i])
                    vis[i] = 1, nn++;
                add(i, sink);
            }
            for(int j = i - 1;j >= 0;j--)
            {
                if(dp[j] + 1 == dp[i]&&num[i] > num[j])
                {
                    add(j, i);
                    if(!vis[i])
                        vis[i] = 1, nn++;
                    if(!vis[j])
                        vis[j] = 1, nn++;
                }
            }
        }
        bfs();
        ans2 = isap();
        printf("%d\n%d\n", ans1, ans2);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值