• 2018 ICPC 沈阳网络预赛 Fantastic Graph (优先队列)


    【传送门】https://nanti.jisuanke.com/t/31447

    【题目大意】:有一个二分图,问能不能找到它的一个子图,使得这个子图中所有点的度数在区间【L,R】之内。

    【题解】首先我们分这几种情况讨论:

    (1)如果集合U,V中存在某个点,它的度数小于L,那么肯定就不满足题意,直接输出No。所以对任意i, degree[i] >= L

    (2)如果集合U,V中所有点的度数都在给定区间内,直接输出Yes。

    (3)如果集合U,V中存在某些点的度数大于R,则需要减少与它关联的边,直到它的度数小于等于R

    那么如何删边呢?我们把某个度数过大的点X的所有终点放入优先队列中,这个队列根据点的度数排好序,度数大的点Y在队首,当X的度数大于R时,我们取出队首Y,如果Y度数大于L,代表可以删边,X,Y的度数均自减1。

    如果X的度数大于R时,队首Y的度已经不能再减(已经小于等于L了),那么就表明找不到这样的子图,输出No。

    把所有的点都按照上述过程扫一遍,看中途是不是会判定找不到这样的子图。

    时间复杂度:O(N*LogN)

    有网上题解说可以使用网络流,暂时记下以后再探讨。

    【AC代码】

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int maxn = 41000;
    vector<int> G[maxn];//存图 
    int offset = 20010;//两个集合标号都从1开始为了区分设置一个偏移量,右边的序号都加上偏移量  
    int degree[maxn];//存度数 
    
    
    // 自定义优先级  按度优先 
    struct cmp  
    {  
        bool operator()(const int &t1,const int &t2)  
        {  
            return degree[t1] < degree[t2];
        }  
    };
    
    //初始化 
    void init(){
        memset(degree , 0, sizeof degree);
        for(int i=0; i<maxn; i++)    G[i].clear();
    }
    
    int main(){
        int n,m,k;
        int l,r;
        int u,v;
        int ca = 1;
        while(scanf("%d %d %d", &n,&m,&k) != EOF){
            init();
            int flag = 1;
            scanf("%d %d",&l, &r);
            //建图,记录度数 
            for(int i=1; i<=k; i++){
                scanf("%d %d",&u, &v);
                G[u].push_back(v+offset);
                G[v+offset].push_back(u);
                degree[u]++;
                degree[v+offset]++;
            }
            //只要有一个点度数小于L就GG 
            for(int i=1 ; i<=n; i++){
            //    cout<<" "<<degree[i]<<endl;
                if(degree[i] < l){
                    flag = 0;
                    break;
                }
            }
            for(int i=1+offset; i<=m+offset; i++){
            //    cout<<" "<<degree[i]<<endl;
                if(degree[i] < l){
                    flag = 0;
                    break;
                }
            }
            if(!flag){
                printf("Case %d: No
    " , ca++);
                continue;
            }
                
            //开始执行步骤(3) 对左边集合所有点  删边减度 
            for(int i=1; i<=n; i++){
                if(flag == 0)    break;
                
                 priority_queue<int,vector<int>,cmp> q; //定义优先队列 
                 while(!q.empty()) q.pop();
                 
                 //对每一个点X的终点入队等待删边 
                 for(int j=0; j<G[i].size(); j++){
                     q.push(G[i][j]);
                }
                //只要这个点 X的度数大于R必须删边减度 
                while(degree[i] > r){
                    int f = 0;
                    //取出队首 
                    int tp = q.top();
                    int t = degree[tp];
                    q.pop();
                    if(t-1 >= l){
                        f = 1;
                        degree[tp] --;
                        degree[i]--;
                        
                    }else{
                        f = 0;
                    }
                    if(degree[tp] >= l+1)
                        q.push(tp);
                    if(f == 0){
                        flag = 0;
                        break;
                    }
                }    
            }
            
            //一样的操作,对右边集合 
            for(int i=1+offset; i<=m+offset; i++){
                if(flag == 0)    break;
                 priority_queue<int,vector<int>,cmp> q;
                 while(!q.empty()) q.pop();
                 
                 for(int j=0; j<G[i].size(); j++){
                     q.push(G[i][j]);
                }
                
                while(degree[i] > r){
                    int f = 0;
                    int tp = q.top();
                    int t = degree[tp];
                    q.pop();
                    if(t-1 >= l){
                        f = 1;
                        degree[tp] --;
                        degree[i]--;
                        
                    }
                    if(degree[tp] >= l+1)
                        q.push(tp);
                    if(f == 0){
                        flag = 0;
                        break;
                    }
                }    
            }
            
            ///最终判定 
            if(flag)    printf("Case %d: Yes
    " , ca++);
            else    printf("Case %d: No
    " , ca++);    
            
        }
    }
  • 相关阅读:
    [NOIP2018 提高组] 保卫王国
    CF 939F. Cutlet
    [USACO15JAN]Moovie Mooving G
    [NOIP2017 提高组] 宝藏
    花园
    [[清华集训2012]串珠子]
    帮助——状压
    R语言产生月末日期
    R for循环示例
    Spark scala String Array转为String
  • 原文地址:https://www.cnblogs.com/czsharecode/p/9612625.html
Copyright © 2020-2023  润新知