• 冒泡排序


    2020.10.4

    题目描述

    有一个 \(n\) 个点的图 \(G\) ,最开始没有边。现有一个 \(1~n\) 的排列,若对于一组 \(i,j\in[1,n]\)\(a_i>a_j\) 则在图 \(G\) 中连一条 \(i\leftrightarrow j\) 的无向边。求图 \(G\) 的最大独立集大小。其中 \(1\leq n\leq 10^5\)

    解法

    反图:图 \(G\) 在完全图上的补集

    独立集:集合中任意两个点间都没有边相连的集合。

    团:集合中任意两个点间都有边相连的集合。

    最大独立集/团:集合元素个数最多的独立集/团。

    性质:无向图的最大独立集等于其反图的最大团。

    证明:显然,在反图中最大团里的点在原图中都没有边相互连接,则这些点即为原图的最大独立集。

    回到此题,若其原图中有连边 \(i\leftrightarrow j\) 则在序列中,\(a_i\)\(a_j\) 一定为逆序对。反过来,如果在反图中有连边则在序列中为顺序对。而最大团中的点都相互有连边,则这些点共同组成了一个上升子序列。那么最大团大小即为最长上升子序列的长度。又有此序列为一个排列,那么显然可以直接用树状数组维护。

    #include<stdio.h>
    #define N 100007
    
    int c[N],n;
    inline int max(int x,int y){return x>y? x:y;}
    inline int lowbit(int x){return x&(-x);}
    inline void add(int x,int v){while(x<=n){c[x]=max(c[x],v);x+=lowbit(x);}}
    inline int query(int x){int ret=0;while(x){ret=max(ret,c[x]);x-=lowbit(x);}return ret;}
    
    inline int read(){
        int x=0,flag=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    int f[N],a[N];
    int main(){
        freopen("sort.in","r",stdin);
        freopen("sort.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++) a[i]=read();
        int ans=0;
        for(int i=1;i<=n;i++){
            f[i]=query(a[i])+1;
            add(a[i],f[i]);
            ans=max(ans,f[i]);
        }
        printf("%d",ans);
    }
    
    /*
    3
    3 1 2
    
    5
    4 1 5 3 2
    */
    
  • 相关阅读:
    IO流
    泛型类
    自动打包&解包:auto-boxing&unboxing
    Map接口
    如何选择数据结构
    Compareable接口
    List常用算法
    equals和==
    List接口
    set接口
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/13767384.html
Copyright © 2020-2023  润新知