网络流24题6 最长递增子序列

31 篇文章 0 订阅

题目描述


给定正整数序列x1,…,xn 。

(1)计算其最长递增子序列的长度s。

(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。

(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列。

设计有效算法完成(1)(2)(3)提出的计算任务。

Analysis


第一问秒掉
第二问比较难想到,每个i节点按f[i]不同分了很多层,那么每次s到t的一条合法路径都是满足条件的序列。由于序列中每个点不可重复,需要拆点。边权是1所以最大流就是增广路的条数,所以最大流量就是第二问结果
第三问特殊地要求x1和xn可以重复使用,那么就取消这两个点相关边的流量限制,求网络最大流即可。

略坑爹,题目求的不是上升序列而是不下降序列
如果要求点不能重复使用,那么拆点是一种可行的方法

洛谷死活不能过,蛋疼

Code


/*
ID:wjp13241
PROG:
LANG:C++
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define dfo(i,a,b) for(int i=a;i>=b;i--)
#define fore(i,x,e) for(int i=ls[x];i;i=e[i].next)
#define fil(x,t) memset(x,t,sizeof(x))
#define FILEIN(s) freopen(s,"r",stdin)
#define FILEOUT(s) freopen(s,"w",stdout)
#define STP system("pause")
#define min(x,y) x<y?x:y
#define max(x,y) x>y?x:y
#define MP(x,y) make_pair(x,y)
#define PuB(v,x) v.push_back(x)
#define PoB(v) v.pop_back()
#define ld long double
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define LIM 100000000
#define EPS 1e-4
#define N 10201
#define E N*20+1
#define ST 0
#define ED N-1
#define L 21
using namespace std;
struct edge{int y, w, next;}e[E];
int num[N], dis[N], cur[N], ls[N], f[N], maxE = 0;
int add(int x, int y, int w){
    e[++maxE] = (edge){y, w, ls[x]};ls[x] = maxE;
    e[++maxE] = (edge){x, 0, ls[y]};ls[y] = maxE;
}
int bfs(int st, int ed){
    queue<int>q;
    q.push(st);
    fil(dis, 63);
    dis[st] = 0;
    while (!q.empty()){
        int now = q.front();q.pop();
        for (int i = ls[now]; i; i = e[i].next){
            if (e[i].w > 0 && dis[now] + 1 < dis[e[i].y]){
                q.push(e[i].y);
                dis[e[i].y] = dis[now] + 1;
                if (e[i].y == ed){
                    return 1;
                }
            }
        }
    }
    return 0;
}
int find(int now, int ed, int mn){
    if (now == ed){
        return mn;
    }
    for (int &i = cur[now]; i; i = e[i].next){
        if (e[i].w > 0 && dis[now] + 1 == dis[e[i].y]){
            int d = find(e[i].y, ed, min(mn, e[i].w));
            if (d > 0){
                e[i].w -= d;
                e[i ^ 1].w += d;
                return d;
            }
        }
    }
    return 0;
}
int build(int n){
    fo(i, 1, n){
        fo(j, i + 1, n){
            if (f[j]  == f[i] + 1 && num[i] <= num[j]){
                add(i + n, j, 1);
            }
        }
    }

}
int dinic(int n){
    int mxFlow = 0;
    while (bfs(ST, ED)){
        fo(i, 1, n + n){
            cur[i] = ls[i];
        }
        cur[ST] = ls[ST];
        cur[ED] = ls[ED];
        mxFlow += find(ST, ED, INF);
    }
    return mxFlow;
}
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>> n;
    fo(i, 1, n)
        cin>> num[i];
    int ans = 0;
    fo(i, 1, n){
        f[i] = 1;
        fo(j, 1, i-1){
            if (num[j] <= num[i]){
                f[i] = max(f[i], f[j] + 1);
            }
            ans = max(ans, f[i]);
        }
    }
    cout<< ans<< endl;

    fo(i, 1, n){
        if (f[i] == 1){
            add(ST, i, 1);
        }
        if (f[i] == ans){
            add(i + n, ED, 1);
        }
        add(i, i+n, 1);
    }
    build(n);

    int mxFlow = dinic(n);
    cout<< mxFlow <<endl;

    fil(ls, 0);
    maxE = 0;
    fo(i, 1, n){
        int v = (i == 1 || i == n)? INF: 1;
        if (f[i] == 1){
            add(ST, i, v);
        }
        if (f[i] == ans){
            add(i + n, ED, v);
        }
        add(i, i+n, v);
    }
    build(n);

    mxFlow = dinic(n);
    cout<< mxFlow <<endl;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值