原题见CF 553D
无向图G,n个点,m条边,k个坏点。
在G中选取子图S(可以不连通),S不能包含坏点。对于每个S中的点x,描述强度值
I=x的属于S的邻居数/x的所有邻居数。
x的邻居是指和他有直接相连的一条边的点。
现在求一个子图S,使得S中的强度值最小的尽量大。
思路
先把所有非坏点的选入图S。从中挑出强度值最小的点x移出S。修改和x相邻的点的强度值。
每次移出点的强度值的大小是无规律的,并不递增或递减。只要取其中的最大的值即可。
同时记录移出顺序,输出最大值开始的之后的所有点即可。
挺贪心的,竟然过了。证明?额,举不出反例。
代码
解释:
a[]:a[i]为1时,表示不在S中。移出点可以视为坏点;
x[]:强度I的分子;
y[]:强度I的分母;
q:优先队列,把S中的点的编号、强度存入。
每次修改时,只在原图中修改,不必在优先队列中修改值,并将新值放入队列。即队列里有旧的无效点和新的修改点。每次从队列中取出值时,判断其是否是最新的,只对新的有操作,否则直接扔了。
/*--------------------------------------------
* File Name: CF 553D
* Author: Danliwoo
* Mail: Danliwoo@outlook.com
* Created Time: 2016-04-28 16:35:59
--------------------------------------------*/
#include <bits/stdc++.h>
#include <vector>
#include <queue>
using namespace std;
#define N 100010
#define eps 1e-8
vector <int> mp[N];
int a[N], x[N], y[N], z[N];
struct node{
int num;
double v;
node(){}
node(int num, double v) : num(num), v(v){}
};
priority_queue <node> q;
bool operator < (node a, node b){
return a.v > b.v;
}
int main(){
int n, m, k;
while(~scanf("%d%d%d", &n, &m, &k)){
memset(mp, 0, sizeof(mp));
memset(a, 0, sizeof(a));
memset(z, 0, sizeof(z));
int cnt = 0;
while(k--){
int t;
scanf("%d", &t);
a[t-1] = 1;
}
while(m--){
int u, v;
scanf("%d%d", &u, &v);
u--; v--;
mp[u].push_back(v);
mp[v].push_back(u);
}
while(!q.empty()) q.pop();
for(int i = 0;i < n;i++){
x[i] = y[i] = mp[i].size();
for(int j = 0;j < y[i];j++) if(a[mp[i][j]])
x[i]--;
node t = node(i, 1.0*x[i]/y[i]);
q.push(t);
}
double ans = -2;
int stop = -1;
while(!q.empty()){
node t = q.top(); q.pop();
int i = t.num;
if(a[i] || fabs(1.0*x[i]/y[i]-t.v) > eps) continue;
if(t.v > ans){
ans = t.v;
stop = i;
}
z[cnt++] = i;
a[i] = 1;
for(int j = 0;j < y[i];j++) if(!a[mp[i][j]]){
int tp = mp[i][j];
x[tp]--;
q.push(node(tp, 1.0*x[tp]/y[tp]));
}
}
for(int i = 0;i < cnt;i++) {
if(z[i] == stop){
stop = i;
break;
}
}
printf("%d\n", cnt-stop);
for(int i = stop;i < cnt;i++)
printf("%d%c", z[i]+1, " \n"[i==cnt-1]);
}
return 0;
}