Codeforces Round #309 (Div. 1)D. Nudist Beach 二分+bfs

题目:http://codeforces.com/contest/553/problem/D

在一个无向图中,有若干坏点,选择一个不包含坏点的集合,使得集合中p值最小的点的p值最大。一个点的p值=集合中与该点相连的点的个数 / 与改点相连的总点数.


思路:考虑到p值是0到1的实数值,可以二分这个最小值。每次判断当前pi是否可以成立,做法是bfs。首先把所有点加进集合中,把不符合情况的点从集合中删除,删除的时候会影响到其他点,所以要维护一个欲删除点的队列,也就是bfs一遍,记住更新答案。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const double eps = 1e-8;
vector<int> g[N];
int n,m,k,vis[N];
int kill[N];
set<int> ss,ans_set;
set<int>::iterator it;
double ans = 0;
int tmp[N];

int ok(double p){
    queue<int> q;
    ss.clear();
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++){
        ss.insert(i);
        tmp[i] = g[i].size();
        if(kill[i]){
            vis[i] = 1;
            q.push(i);
        }
    }
    while(!q.empty()){
        int wt = q.front() ; q.pop();
        ss.erase(wt);
        for(int i=0;i<g[wt].size();i++){
            int u = g[wt][i];
            if(vis[u]) continue;
            tmp[u]--;
            double hc = 1.0*tmp[u]/g[u].size();
            if(hc<p){
                vis[u] = 1;
                q.push(u);
            }
        }
    }
    int fl = 1;
    for(it=ss.begin();it!=ss.end();it++)
        if(1.0*tmp[*it]/g[*it].size() < p){ fl = 0 ; break;}

    if(fl && ss.size()>0){
        if(p>ans){
            ans = p;
            ans_set = ss;
        }
        return 1;
    }
    return 0;
}

int main(){
    cin >> n >> m >> k;
    ans_set.clear();
    memset(kill,0,sizeof(kill));
    for(int i=0;i<=n;i++) g[i].clear();
    for(int i=1;i<=k;i++){
        int x;
        scanf("%d",&x);
        kill[x] = 1;
    }
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }

    int co = 0;
    double high=1.0 , low = 0.0;
    while(co<40){
        co++;
        double mid = (high + low)/2;
        if( ok(mid) ) low = mid;
        else high = mid;
    }

    if(ans_set.size()==0){
        cout<<1<<endl;
        for(int i=1;i<=n;i++) if(!kill[i])
        {
            cout<<i<<endl;
            break;
        }
    }
    else
    {
        cout<<ans_set.size()<<endl;
        for(it=ans_set.begin();it!=ans_set.end();it++)
            cout<<*it<<" ";
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值