嘘~ 正在从服务器偷取页面 . . .

洛谷P1285 队员分组 题解


洛谷P1285 队员分组 题解

题目链接:P1285 队员分组

题意

\(n\) 个人从 \(1\)\(n\) 编号,相互之间有一些认识关系,你的任务是把这些人分成两组,使得:

  • 每个人都被分到其中一组。
  • 每个组都至少有一个人。
  • 一组中的每个人都认识其他同组成员。

在满足上述条件的基础上,要求两组成员的人数之差(绝对值)尽可能小。请构造一种可行的方案。

请注意,\(x\) 认识 \(y\) 不一定说明 \(y\) 认识 \(x\)\(x\) 认识 \(y\)\(y\) 认识 \(z\) 不一定说明 \(x\) 认识 \(z\)。即认识关系是单向且不可传递的。

输入格式

输入的第一行是一个整数,代表总人数 \(n\)

\(2\) 到第 \((n + 1)\) 行,每行有若干个互不相同的整数,以 \(0\) 结尾,第 \((i + 1)\) 行的第 \(j\) 个整数 \(a_{i, j}\)\(0\) 除外)代表第 \(i\) 个人认识 \(a_{i, j}\)

输出格式

本题存在 Special Judge。

如果无解,请输出一行一个字符串 No solution

如果有解,请输出两行整数,分别代表两组的成员。每行的第一个整数是该组的人数,后面以升序若干个整数代表该组的成员编号,数字间用空格隔开。

数据范围

保证 \(2 \leq n \leq 100\)\(1 \leq a_{i, j} \leq n\)

考虑建立原图关于 \(n\) 阶完全图的补图,然后把补图的边全部改成无向边。

这样每条边 \((u,v)\) 就意味着 \(u,v\) 必须分到不同的集合内。

考虑设 \(f(i,j)\) 为前 \(i\) 个连通块能否达到 \(0/1\) 的大小,那么显然 \[ f(i,j) = f(i - 1,\, j - \mathrm{size}(i,0)) \lor f(i - 1,\, j - \mathrm{size}(i,1)) \] 显然答案要求最大的 \(j~(1 \le j \le \frac{n}{2})\) 满足 \(f(k, j) = \mathrm{True}\)

其中 \(k\) 表示连通块的总个数,这样可以保证差的绝对值尽可能小。

时间复杂度 \(\mathcal{O}(n^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define pb push_back
#define N ((int)(114))

bitset<N> used, g[N], dp[N], type[N];
int n, cnt, ans, col[N], res[N];
vector<int> vec[N][2];
void dfs(int u, int fa, int c)
{
    vec[cnt][col[u] = c].pb(u);
    rep(v, 1, n) if(v != u && v != fa && !g[v][u])
    {
        if(!(~col[v])) dfs(v, u, c ^ 1);
        else if(col[v] == c) { cout << "No solution\n"; exit(0); }
    }
}
void solve()
{
    dp[0][0] = true;
    rep(i, 1, cnt) rep(j, 0, n / 2)
    {
        int t = j - vec[i][0].size();
        if(t >= 0 && dp[i - 1][t]) { dp[i][j] = true, type[i][j] = 0; }
        t = j - vec[i][1].size();
        if(t >= 0 && dp[i - 1][t]) { dp[i][j] = true; type[i][j] = 1; }
    }
    Rep(i, n / 2, 0) if(dp[cnt][i]) { ans = i; break; }
}
void output()
{
    int t = ans; 
    Rep(i, cnt, 1)
    {
        for(auto j : vec[i][type[i][t]])
            used[res[++res[0]] = j] = true;
        t -= vec[i][type[i][t]].size();
    }
    sort(res + 1, res + 1 + res[0]);
    cout << res[0] << ' ';
    rep(i, 1, res[0]) cout << res[i] << " \n"[i == res[0]];
    cout << n - res[0] << ' '; t = 0;
    rep(i, 1, n) if(!used[i]) cout << i << " \n"[++t == n - res[0]];
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int x; cin >> n;
    rep(i, 1, n) while(cin >> x && x) g[i][x] = true;
    rep(i, 1, n) rep(j, i + 1, n)
        if(!g[i][j] || !g[j][i]) { g[i][j] = g[j][i] = false; }
    memset(col, -1, sizeof(col));
    rep(i, 1, n) if(!(~col[i])) { ++cnt, dfs(i, 0, 1); }
    solve(); output();
    return 0;
}

双倍经验:UVA1627 团队分组 Team them up!


参考文献

[1] https://www.luogu.com.cn/article/xb1agscu


题外话

听完这首歌,智商增加了。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录