Codeforces 553D Nudist Beach 优先队列 贪心

原题见CF 553D

无向图G,n个点,m条边,k个坏点。
在G中选取子图S(可以不连通),S不能包含坏点。对于每个S中的点x,描述强度值

I=xS/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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值