题目: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;
}