HDU3739 Anti LIS 最大流Dinic

针对给定序列,通过最少删除元素使最长递增子序列(LIS)变短的问题,采用拆点与最大流算法求解。先计算LIS长度,然后通过构建图模型并运用最大流最小割原理确定需删除的元素数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Anti LIS
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 577    Accepted Submission(s): 144


Problem Description
Haven't you heard about Lost?
Having written a article named <Summaries of ALL Algorithms>, Lost is good at solved by algorithm problems(?). One day, GXX asked Lost to work out the Longest Increasing Subsequence(for short, LIS) of a given sequence {A_1, A_2, ..., A_N}. Knowing this problem well, Lost simply copied a program from his article and solved the problem in seconds. So that GXX became frustrated. She wanted to cheat Lost by removing some elements from the original sequence to make Lost's answer go wrong. For convinience, she would like to remove least number of elements.


Input
The beginning of the input is an integer T(T <= 10), which is the number of test cases. T cases are followed. The first line of each test case is an integer N (1 <= N <= 1,000), which denotes the length of the sequence. The second line is N integer A_1, A_2, ..., A_N, which denote the given sequence.


Output
For each test case, print a line contains a single integer which is the minimum number of the removed elements.


Sample Input

1
6
10 10 20 1 2 2



Sample Output

2



Author
ftiasch & Lost


Source
ACMDIY第二届群赛 

做完想吐血…搞了接近2天 好不容易思路正确了 还无限TLE…..
原来是自己写的Dinic板子太慢….换了个O(VF)的FF就过了..


http://blog.youkuaiyun.com/LuRiCheng/article/details/54615690有点异曲同工
题意:给定序列 删除最少的数使得LIS长度变短
将每个数看做一个点 首先考虑拆点 每个点转化为 一个入点 和 一个出点
入点 有一条权值为1的边指向 出点
那最少删除多少个点 就转化为最少删除多少条边 可以用最大流求解
再考虑最大流最小割定理
假设LIS长度为len
如果将所有长度为len的路径上的边保留 其余所有边删除掉 那对这个剩余的图的最小割(破坏所有长度为len的路径) 就是使得原图LIS变短需要删除的点数

所以原问题就转化为筛选重要边,拆点,并求一遍最大流
但是问题来了 如果像HDU2485那样跑一遍Floyd显然会超时
考虑dis[i]为以第i个数结尾的LIS长度
但是如果源点只连接dis[i]=1的点,汇点只连接dis[i]=len的点 直接跑最大流 那就可以直接跑出结果,省去筛选边的过程

//新Dinic
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<time.h>
#include<math.h>
#include<list>
#include<cstring>
#include<fstream>
//#include<memory.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define INF 1000000007
#define pll pair<ll,ll>
#define pid pair<int,double>

const int N = 2*1e3 + 10;//点数
const int M = 2*1e6;//边数*2(包括反向边)

int level[N];//标号 level[i]:s到i的最短距离

struct Edge{
    int to,c,next;
}edge[M];
int head[N];

int elem[N];

inline void add_edge(int k,int u,int v,int c){
    edge[k].to = v;
    edge[k].c = c;
    edge[k].next = head[u];
    head[u] = k;
}

bool bfs(int s,int t,int n){//标号 计算level
    deque<int>que;
    fill(level,level+n+1,-1);
    que.push_back(s);
    level[s] = 0;
    while(!que.empty()){
        int u = que.front();
        if(u == t){
            return true;
        }
        que.pop_front();
        for(int i = head[u];i!=-1;i = edge[i].next){
            if(edge[i].c > 0 && level[edge[i].to] == -1){
                level[edge[i].to] = level[u] + 1;
                que.push_back(edge[i].to);
            }
        }
    }
    return false;
}

int dfs(int u,int t,int maxf){//u:所在的点 t:汇点 maxf:能流到u的流量
    if(u == t){
        return maxf;
    }
    int sum = 0;
    for(int i = head[u];i!=-1;i = edge[i].next){
        Edge&e = edge[i];
        if(e.c>0 && level[e.to]==level[u]+1){
            int f = dfs(e.to,t,min(maxf - sum,e.c));
            sum += f;
            edge[i].c -= f;
            edge[i^1].c += f;
            if(sum == maxf){//流量用完了
                break;
            }
        }
    }
    level[u]=-1;
    return sum;
}

int dinic(int s,int t,int n){//s:源点 t:汇点 n:点数
    int ans = 0;
    while(bfs(s,t,n)){
        ans += dfs(s,t,2*INF);
    }
    return ans;
}
int dis[N];
void built_G(int n){
    fill(head,head+2*(n+1)+1,-1);
    int tmp=0;
    for(int i=1;i<=n;++i){//拆点
        add_edge(tmp++,i,i+n,1);
        add_edge(tmp++,i+n,i,0);
    }
    int maxlen=0;
    for(int i=1;i<=n;++i){
        dis[i]=1;
        for(int j=1;j<i;++j){
            if(elem[j]<elem[i]){
                dis[i]=max(dis[i],dis[j]+1);
            }
        }
        maxlen=max(maxlen,dis[i]);
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<i;++j){
            if(elem[j]<elem[i]&&dis[j]+1==dis[i]){
                add_edge(tmp++,j+n,i,1);
                add_edge(tmp++,i,j+n,0);
            }
        }
    }
    for(int i=1;i<=n;++i){//2*n+1:s   2*n+2:t
        if(dis[i]==maxlen){
            add_edge(tmp++,i+n,2*n+2,1);
            add_edge(tmp++,2*n+2,i+n,0);
        }
        if(dis[i]==1){
            add_edge(tmp++,2*n+1,i,1);
            add_edge(tmp++,i,2*n+1,0);
        }
    }
}


int main()
{
    //freopen("/home/lu/Documents/r.txt","r",stdin);
    //freopen("/home/lu/Documents/w.txt","w",stdout);
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&elem[i]);
        }
        built_G(n);
        printf("%d\n",
               dinic(2*n+1,2*n+2,2*n+2));
    }
    return 0;
}
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<time.h>
#include<math.h>
#include<list>
#include<cstring>
#include<fstream>
//#include<memory.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define INF 1000000007
#define pll pair<ll,ll>
#define pid pair<int,double>

const int N = 2*1e3 + 10;//点数
const int M = 2*1e6;//边数

struct Edge{
    int to,c,next;
}edge[M];
int head[N];

int elem[N];

inline void add_edge(int k,int u,int v,int c){
    edge[k].to = v;
    edge[k].c = c;
    edge[k].next = head[u];
    head[u] = k;
}

bool used[N];
int dfs(int v,int t,int f){
    if(v==t)
        return f;
    used[v]=true;
    for(int i=head[v];i!=-1;i=edge[i].next){
        Edge&e=edge[i];
        if(used[e.to]==false&&e.c>0){
            int d=dfs(e.to,t,min(f,e.c));
            if(d>0){
                e.c-=d;
                edge[i^1].c+=d; //给反向边增加流量
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s,int t){//s源点 t汇点
    int flow=0;
    while(true){
        fill(used,used+N+1,false);
        int f=dfs(s,t,INF);
        if(f==0)
            return flow;
        flow+=f;
    }
}

int dis[N];
void built_G(int n){
    fill(head,head+2*(n+1)+1,-1);
    int tmp=0;
    for(int i=1;i<=n;++i){//拆点
        add_edge(tmp++,i,i+n,1);
        add_edge(tmp++,i+n,i,0);
    }
    int maxlen=0;
    for(int i=1;i<=n;++i){//计算dis
        dis[i]=1;
        for(int j=1;j<i;++j){
            if(elem[j]<elem[i]){
                dis[i]=max(dis[i],dis[j]+1);
            }
        }
        maxlen=max(maxlen,dis[i]);
    }
    for(int i=1;i<=n;++i){//添加内点之间的边
        for(int j=1;j<i;++j){
            if(elem[j]<elem[i]&&dis[j]+1==dis[i]){
                add_edge(tmp++,j+n,i,1);
                add_edge(tmp++,i,j+n,0);
            }
        }
    }
    for(int i=1;i<=n;++i){//s:2*n+1  t:2*n+2
        if(dis[i]==maxlen){//连接到汇点
            add_edge(tmp++,i+n,2*n+2,1);
            add_edge(tmp++,2*n+2,i+n,0);
        }
        if(dis[i]==1){//连接到源点
            add_edge(tmp++,2*n+1,i,1);
            add_edge(tmp++,i,2*n+1,0);
        }
    }
}


int main()
{
    //freopen("/home/lu/Documents/r.txt","r",stdin);
    //freopen("/home/lu/Documents/w.txt","w",stdout);
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&elem[i]);
        }
        built_G(n);
        printf("%d\n",
               max_flow(n*2+1,n*2+2));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值