SCUTCODE.124 笔芯子序列 【最大流最小割】

博客详细介绍了如何运用最大流最小割算法解决寻找正整数序列的最小bx子序列长度问题。首先根据序列构建图,然后通过Floyd算法求最长路,再通过最大流找到最小割,最后通过拆点处理来满足题目要求,找出需要删除的点数。

题目描述

对于一个nn个数的正整数序列AA,定义它的bxbx子序列为 p_1,p_2,\cdots,p_kp1,p2,,pk,满足

1 \leq p_1 < p_2 <\ ...\ <p_k \leq n, A_{p_i} \equiv 0 (mod\ A_{p_{i-1}}),2 \leq i \leq k1p1<p2< ... <pkn,Api0(mod Api1),2ik

对于AA,假设它的最长 bxbx 子序列长度为kk,它的一个 bxbx子序列为
q_1,q_2,\cdots,q_tq1,q2,,qt,使得删掉 A_{q_1}, A_{q_2},...,A_{q_t}Aq1,Aq2,...,Aqt 之后,AA的最长 bxbx 子序列长度小于 kk

给定一个 nn 个数的正整数序列 AA,你需要求出它的最小 bxbx 子序列的长度。


输入格式

输入第一行一个整数 TT,表示数据组数。
对于每组数据,第一行一个整数 nn
第二行nn个整数,表示序列 AA
1 \leq T \leq 201T20
1 \leq n \leq 1001n100
1 \leq A_i \leq 10000000001Ai1000000000


输出格式

对每组数据,输出它的最小 bxbx 子序列的长度。


样例数据

输入

2
4
1 3 2 6
6
2 4 8 3 9 27

输出

1
2

若a[i]%a[j]==0,(j<i),连接一条从j到i的边,

再添加源汇点S,T,建立图G

跑一遍Floyd求最长路maxDis

对边(u,v),如果dis[s][u]+dis[v][t]+1==maxDis

那通过(u,v)边可能走出最长路,将(u,v)添加进图G'中

此时跑一遍最大流,即可得到G'的最小割:删除最少多少条边,使得最长路变短

但是原题要求的是删除多少个点

于是拆点跑最大流即可 

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<string.h>
#include<math.h>
#include<list>

using namespace std;

#define ll long long
#define pii pair<int,int>
const int inf = 1e9 + 7;

const int N = 2*100+5;
const int M = N*N*2;

int a[N];

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

int level[N];

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

bool bfs(int s,int t,int n){
    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){
    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){
    int ans=0;
    while(bfs(s,t,n)){
        ans+=dfs(s,t,2*inf);
    }
    return ans;
}

int dis[N][N];

void floyd(int n){
    for(int k=0;k<n;++k){
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                if(dis[i][k]>0&&dis[k][j]>0){
                    dis[i][j]=max(dis[i][k]+dis[k][j],dis[i][j]);
                }
            }
        }
    }
}

vector<pii>vec;

int main()
{
    //freopen("/home/lu/Documents/r.txt","r",stdin);
    //freopen("/home/lu/Documents/w.txt","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        vec.clear();
        int n;
        scanf("%d",&n);
        int s=2*n,t=s+1;
        memset(dis,0,sizeof(dis));
        for(int i=0;i<n;++i){
            scanf("%d",&a[i]);
            dis[s][i]=1;
            vec.push_back({s,i});
            dis[i+n][t]=1;
            vec.push_back({i+n,t});
            dis[i][i+n]=1;
            vec.push_back({i,i+n});
        }
        for(int i=0;i<n;++i){
            for(int j=0;j<i;++j){
                if(a[i]%a[j]==0){
                    dis[j+n][i]=1;
                    vec.push_back({j+n,i});
                }
            }
        }
        floyd(t+1);
        int maxDis=dis[s][t];
        fill(head,head+t+1,-1);
        int nume=0;
        for(int i=0;i<vec.size();++i){
            int u=vec[i].first,v=vec[i].second;
            if(dis[s][u]+dis[v][t]+1==maxDis){
                addEdge(nume++,u,v,1);
                addEdge(nume++,v,u,0);
            }
        }
        printf("%d\n",dinic(s,t,t+1));
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值