[SDOI2019] 热闹的聚会与尴尬的聚会

[SDOI2019] 热闹的聚会与尴尬的聚会

讲真的,

这道题我本来没看懂,

后来看到题解里说是至少 \(p\) 的时候我才意识到我读错题了...

既然是至少 \(p\) ,

那就简单多了.

我们先仔细看一下题,

我们发现 \(p\)\(q\) 都是越大越好,

而且是都大最好,

因为两个不等式里的 \(p\)\(q\) 都是一个在分子而另一个在分母.

那我们就先想一下第一个问题,

如何求一个尽可能大的 \(p\) .

通过读题我们发现,

\(p\) 的大小取决于当前图中点权最小的一个点的点权.

所以我们就可以贪心的每次找点权最小的点,

然后删掉它和它相连的边,

如果删除之后的点权最小的点的点权比当前的最小点权要大,

那么它就是一个更优解.

我们再看第二个问题,

很显然,

是让我们求一个独立集,

但是不一定非得是最大独立集,

所以我们就可以贪心一波,

每次找到点权最小的点,

删除它和与它相连的点,

保留最后的图,

最后剩下的点就是一个近似的最大独立集.

我们发现,

这两个问题的解决过程非常的相似,

都是从点权最小的点开始删除,

一个是删除相连的边,

另一个是删除相连的点,

所以我们就可以把这两个问题写到一个过程里.

然后就有了下面的代码.

code:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int read(){
    int x = 0;
    char ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)){
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x;
}
const int N = 1e4 + 5, M = 2e5 + 5;
int T, n, m;
int tot, to[M], nxt[M], head[N];
int du[N], ans1, ans2, tot1, tot2, sat[N], sun[N];
priority_queue <pair <int, int > > q;
bool flag[N];
void add(int u, int v){
    to[++tot] = v, nxt[tot] = head[u], head[u] = tot;
}
void init(){
    n = read(), m = read();
    while (q.size()) q.pop();
    memset(du, 0, sizeof(int) * (n + 1));
    memset(head, 0, sizeof(int) * (n + 1));
    memset(flag, 0, sizeof(int) * (n + 1));
    ans1 = ans2 = tot = tot1 = tot2 = 0;
    for (int i = 1; i <= m; i++){
        int u = read(), v = read();
        add(u, v); add(v, u);
        du[u]++, du[v]++;
    }
}
void solve(){
    for (int i = 1; i <= n; i++) q.push(make_pair(-du[i], i));
    while (q.size()){
        int x = q.top().second; q.pop();
        if (flag[x]) continue;
        if (du[x] > ans1 && !tot1){
            ans1 = du[x];
            for (int i = 1; i <= n; i++){
                if (!flag[i]) sat[++tot1] = i;
            }
        }
        sun[++tot2] = x;
        flag[x] = 1;
        for (int i = head[x]; i; i = nxt[i]){
            int y = to[i];
            if (flag[y]) continue;
            flag[y] = 1;
            du[x]--, du[y]--;
        }
        q.push(make_pair(-du[x], x));
    }
}
void print(){
    printf("%d ", tot1);
    for (int i = 1; i <= tot1; i++) printf("%d ", sat[i]);
    printf("\n");
    printf("%d ", tot2);
    for (int i = 1; i <= tot2; i++) printf("%d ", sun[i]);
    printf("\n");
}
int main(){
    T = read();
    while (T--){
        init();
        solve();
        print();
    }
    return 0;
}

woc!!!

过样例了!

我过样例了兄弟!

80兄弟!

我这乱写的代码居然80!

也没仔细检查一下,

甚至没有严谨证明,

反正本来就是求近似解,

贪心就是了.

然后我看了一下错误信息,

我的正确性是没有问题的,

意思就是我的代码求出的解一定符合这两个问题,

但是它的 \(p\)\(q\) 不一定符合条件,

所以我就开始想,

如何让 \(p\) 或者 \(q\) 更大一些...

突然!!!

很快啊!!!

很快我就发现了!!!

我这个代码有问题!

        if (du[x] > ans1 && !tot1){
            ans1 = du[x];
            for (int i = 1; i <= n; i++){
                if (!flag[i]) sat[++tot1] = i;
            }
        }

注意这个if,

有没有发现这个if只能运行一次!

然后我就有了一个大胆的想法...

before:

    printf("%d ", tot1);
    for (int i = 1; i <= tot1; i++) printf("%d ", sat[i]);

after:

    printf("%d ", n);
    for (int i = 1; i <= n; i++) printf("%d ", i);

哈哈哈草,

笑死我了,

我直接输出 \(1 \sim n\) 居然80分哈哈哈哈!

然后我就想了一下,

随机图中的 \(p\) 好像不会太小,

\(q\) 也是,

但是仍然是80,

我想要在独立集上再下功夫难度比较大,

因为最大独立集是npc问题,

而其他近似解都是诸如模拟退火,二分之类的.

所以我就只能在第一个问题上下功夫.

然后我按照最初的思路重新写了一份代码,

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int read(){
    int x = 0;
    char ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)){
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x;
}
const int N = 1e4 + 5, M = 2e5 + 5;
int T, n, m;
int tot, to[M], nxt[M], head[N];
int du[N], num, ans1, ans2, sat[N], sun[N];
priority_queue <pair <int, int > > q;
bool flag[N];
void add(int u, int v){
    to[++tot] = v, nxt[tot] = head[u], head[u] = tot;
}
void init(){
    n = read(), m = read();
    while (q.size()) q.pop();
    memset(du, 0, sizeof(int) * (n + 1));
    memset(head, 0, sizeof(int) * (n + 1));
    memset(flag, 0, sizeof(int) * (n + 1));
    num = ans1 = ans2 = tot = 0;
    for (int i = 1; i <= m; i++){
        int u = read(), v = read();
        add(u, v); add(v, u);
        du[u]++, du[v]++;
    }
}
void solve(){
    bool qwq = 1;
    for (int i = 1; i <= n; i++) q.push(make_pair(-du[i], i));
    while (q.size()){
        int x = q.top().second; q.pop();
        if (du[x] > num && qwq){
            num = du[x];
            for (int i = 1; i <= n; i++){
                if (!flag[i]) sat[++ans1] = i;
            }
        }
        else qwq = 0;
        if (flag[x]) continue;
        sun[++ans2] = x;
        flag[x] = 1;
        for (int i = head[x]; i; i = nxt[i]){
            int y = to[i];
            if (flag[y]) continue;
            flag[y] = 1;
            du[x]--, du[y]--;
            q.push(make_pair(-du[y], y));
        }
    }
}
void print(){
    printf("%d ", ans1);
    for (int i = 1; i <= ans1; i++) printf("%d ", sat[i]);
    printf("\n");
    printf("%d ", ans2);
    for (int i = 1; i <= ans2; i++) printf("%d ", sun[i]);
    printf("\n");
}
int main(){
    T = read();
    while (T--){
        init();
        solve();
        print();
    }
    return 0;
}

草,

爆零了,

淦!

然后我突然发现,

我的ans1好像会一直+,

而不会更新,

于是就成了这个样子:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int read(){
    int x = 0;
    char ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)){
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x;
}
const int N = 1e4 + 5, M = 2e5 + 5;
int T, n, m;
int tot, to[M], nxt[M], head[N];
int du[N], num, ans1, ans2, sat[N], sun[N];
priority_queue <pair <int, int > > q;
bool flag[N];
void add(int u, int v){
    to[++tot] = v, nxt[tot] = head[u], head[u] = tot;
}
void init(){
    n = read(), m = read();
    while (q.size()) q.pop();
    memset(du, 0, sizeof(int) * (n + 1));
    memset(head, 0, sizeof(int) * (n + 1));
    memset(flag, 0, sizeof(int) * (n + 1));
    num = ans1 = ans2 = tot = 0;
    for (int i = 1; i <= m; i++){
        int u = read(), v = read();
        add(u, v); add(v, u);
        du[u]++, du[v]++;
    }
}
void solve(){
    for (int i = 1; i <= n; i++) q.push(make_pair(-du[i], i));
    while (q.size()){
        int x = q.top().second; q.pop();
        if (du[x] > num){
            num = du[x]; ans1 = 0;
            for (int i = 1; i <= n; i++){
                if (!flag[i]) sat[++ans1] = i;
            }
        }
        if (flag[x]) continue;
        sun[++ans2] = x;
        flag[x] = 1;
        for (int i = head[x]; i; i = nxt[i]){
            int y = to[i];
            if (flag[y]) continue;
            flag[y] = 1;
            du[x]--, du[y]--;
            q.push(make_pair(-du[y], y));
        }
    }
}
void print(){
    printf("%d ", ans1);
    for (int i = 1; i <= ans1; i++) printf("%d ", sat[i]);
    printf("\n");
    printf("%d ", ans2);
    for (int i = 1; i <= ans2; i++) printf("%d ", sun[i]);
    printf("\n");
}
int main(){
    T = read();
    while (T--){
        init();
        solve();
        print();
    }
    return 0;
}

不抱希望的交了,

然后就A了...

其实这个算法的复杂度上限很大,

但是可能是由于随机数据加上近似解,

所以跑的并不慢.

这里就告诉我们要有梦想!

万一就过了呢!

posted @ 2021-08-22 16:34  sshadows  阅读(49)  评论(0编辑  收藏  举报