• “知乎杯”2018 CCF 大学生计算机系统与程序设计竞赛 贪心算法(greedy)


    -->

    贪心算法

    1)题解

            分别用V0V1V>=2表示度为01以及至少为2的顶点集合

            对于每个顶点,维护三个属性:

            degree 邻居的个数

            degree2 邻居中度为2的顶点数

            id     编号

    Pseudo-code

            initialize V0, V1, V>=2 and (degree, degree2, id) of each node

            while G is not empty

            if V0 is not empty

            choose v ∈ V0 with the smallest id

            output v, delete v from V0 and G

            else if V1 is not empty

            choose v ∈ V1 with the smallest id, and find the neighbor u of v

            output v, delete v from V0 and G, delete u from V1(or V>=2) and G

            else

            choose v ∈ V>=2 with the largest (degree, degree2, id)

            delete v from V>=2 and G

    索引的维护

            需要注意的是,每个顶点的属性以及顶点集合V0V1V>=2并非一成不变。

            当从图中删去某个顶点u时,u邻居的degree均会减一;如果udegree恰好为2,那么u邻居的degree2也会减一。

            如果某个邻居vdegree恰好从3减少到2或从2减到1,那么还会进一步影响到v的邻居的degree2属性。

            对于那些degree减一的顶点,还需要相应地更新V0V1V>=2

    Delete Node u from G

            for v ∈ Neighbor(u)

            v.degree decreases by one

            if u.degree == 2 then v.degree2 decreases by one

            if v.degree == 0

            move v from V1 to V0

            else if v.degree == 1

            move v from V>=2 to V1

            find the only neighbor w of v

            w.degree2 decreases by one

            else if v.degree == 2

            for w ∈ Neighbor(v) do w.degree2 increases by one

    2)复杂度分析

    V0V1 {node_id}

            插入、删除顶点,但每个顶点最多一次;

            查询id最小的顶点。

            顶点数量O(N)

            插入删除操作次数O(N)

            查询最值次数O(N)

    V>=2   {<node_id, value>}

            初始化之后不会再插入顶点;

            删除顶点,每个最多一次;

            查询属性值最大的顶点;

            修改一个点的属性值。

            顶点数量O(N)

            删除操作次数O(N)

            查询最值次数O(N)

            修改属性次数O(M)

    3)可以使用的数据结构

            堆、线段树、平衡树等等……

            增、删、改、查都可以在O(logN)复杂度内完成

            时间复杂度:O((N+M)logN)

            懒得敲一个的话就用经济实惠的priority_queue好了。虽然不能直接进行删除或修改,但可以用懒更新的方式解决。即等到取用最值的时候,再判断该顶点是否已经被删除或属性已经被修改过了。

     

     

    #include<queue>
    #include<cstdio>
    const int N=1e5+5;
    const int M=N*10;
    template <typename T>
    inline void read(T &x){
        T f=1;char ch=getchar();x=0;
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    struct node{
        int id,degree,nxt2;
        node(int id=0,int degree=0,int nxt2=0):id(id),degree(degree),nxt2(nxt2){}
        bool operator <(const node &a){   // id < nxt2 < degree
            return degree!=a.degree?degree<a.degree:nxt2!=a.nxt2?nxt2<a.nxt2:id<a.id;
        }
    }b[N];
    template<class nodeT> 
    struct segtree{
        int n;nodeT* a;
        segtree(){}
        segtree(int _n,const nodeT* b):n(_n){
            a=new nodeT[n<<2|1];
            build(1,1,n,b);
        }
    #define lch k<<1
    #define rch k<<1|1
    #define max(a,b) ((a)<(b)?b:a)
        inline void updata(int k){
            a[k]=max(a[lch],a[rch]);
        }
        void build(int k,int l,int r,const nodeT* b){
            if(l==r){
                a[k]=b[l];
                return ;
            }
            int mid=l+r>>1;
            build(lch,l,mid,b);
            build(rch,mid+1,r,b);
            updata(k);
        }
        void change(int k,int l,int r,int p,const nodeT &v){
            if(l==r){
                a[k]=v;
                return ;
            }
            int mid=l+r>>1;
            if(p<=mid) change(lch,l,mid,p,v);
            else change(rch,mid+1,r,p,v);
            updata(k);
        }
        inline void change(int p,const nodeT &v){
            change(1,1,n,p,v);
        }
        inline void del(int p){
            nodeT t(-1,-1,-1);
            change(1,1,n,p,t);
        }
        inline nodeT& query(){
            return a[1];
        }
    #undef lch
    #undef rch
    #undef max(a,b)
    };
    int cnt,ans[N];
    int n,m,tot,to[M],next[M],head[N],degree[N];int del[N];
    std::priority_queue<int,std::vector<int>,std::greater<int> >q0,q1;
    inline void add(int x,int y){
        to[++tot]=y;next[tot]=head[x];head[x]=tot;
    }
    inline void addedge(int x,int y){
        add(x,y);degree[x]++;
        add(y,x);degree[y]++;
    }
    inline void Erase(int u,segtree<node> &tree){
        del[u]=1;
        tree.del(u);
        for(int j=head[u],v;j;j=next[j]){
            if(del[v=to[j]]) continue;
            b[v].degree--;
            if(b[u].degree==2) b[v].nxt2--;
            tree.change(v,b[v]);
            if(b[v].degree==2||b[v].degree==1){
                for(int k=head[v],w;k;k=next[k]){
                    if(del[w=to[k]]) continue;
                    b[w].nxt2+=b[v].degree*2-3;
                    tree.change(w,b[w]);
                }
            }
            if(b[v].degree==0) q0.push(v);
            if(b[v].degree==1) q1.push(v);
        }
    }
    inline void Init(){
        read(n);read(m);
        for(int i=1,x,y;i<=m;i++) read(x),read(y),addedge(x,y);
    }
    inline void Solve(){
        for(int i=1;i<=n;i++) b[i].id=i,b[i].degree=degree[i],b[i].nxt2=0;
        for(int i=1;i<=n;i++){
            if(degree[i]==2){
                for(int j=head[i];j;j=next[j]){
                    b[to[j]].nxt2++;
                }
            }
        }
        segtree<node> tree(n,b);
        for(int i=1;i<=n;i++){
            if(!degree[i]){
                del[i]=1;
                tree.del(i);
                ans[++cnt]=i;
            }else if(degree[i]==1) q1.push(i);
        }
        for(int u,v;;){
            if(!q0.empty()){
                u=q0.top();q0.pop();
                if(del[u]) continue;
                del[u]=1;
                tree.del(u);
                ans[++cnt]=u;
            }
            else if(!q1.empty()){
                u=q1.top();q1.pop();
                if(del[u]) continue;
                for(int i=head[u];i;i=next[i]) if(!del[v=to[i]]) break;
                Erase(u,tree);
                Erase(v,tree);
                ans[++cnt]=u;
            }
            else{
                int tid=tree.query().id;
                if(~tid) Erase(tid,tree);else break;
            }
        }
        for(int i=1;i<=cnt;i++) printf("%d
    ",ans[i]);
    }
    int main(){
        freopen("greedy.in","r",stdin);
        freopen("greedy.out","w",stdout);
        Init();
        Solve();
        return 0;
    }

     

     

  • 相关阅读:
    瑞士军刀DLib的VS2015编译
    win10编译libpng
    win10编译zlib
    win10编译jpeglib
    Hough Transform直线检测
    html+css简单的实现360搜索引擎首页面
    HTML和css简单日常总结
    MySQL中的分区(六)KEY分区
    CentOS 8 安装vsftpd 服务器
    linux负载过高 排查方法及说明 附:Centos安装iostat
  • 原文地址:https://www.cnblogs.com/shenben/p/11614870.html
Copyright © 2020-2023  润新知