ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

真正的骗子(种类并查集)

2022-08-28 21:01:43  阅读:166  来源: 互联网

标签:dep int res 查集 merge 种类 骗子 集合 dp


题目链接

  思路:
    分成两类:1.村民说真话,2.村民说假话。当村民说是好人的时候,有两种情况,他们都是好人和都是坏人。所以将\(a\ + \ (x + y) ,\ b\ +\ (x + y)\)和\(a, b\)合并为一个集合。同理将\(a,b+(x + y)\)和\(a + (x + y), b\)合并为一个集合。
    这个合并的过程我们就可以采用种类并查集的方式来将他们合并起来.

        std::function<int(int)> find = [&] (int x) -> int {
            return x == f[x] ? f[x] : f[x] = find(f[x]);  
        };
            
        auto merge = [&] (int u, int v) -> void {
            u = find(u), v = find(v);
            if (u == v) return ;
            //按秩合并
            if (dep[u] > dep[v]) f[v] = u, s[u] += s[v];
            else if (dep[u] < dep[v]) f[u] = v, s[v] += s[u];
            else f[v] = u, s[u] += s[v], dep[u]++;
        };
        
        //种类并查集合并
        for (int i = 1; i <= n; i++) {
            int a, b;
            char op[5];
            std::cin >> a >> b >> op;
            if (op[0] == 'y') merge(a, b), merge(a + res, b + res);
            else merge(a, b + res), merge(a + res, b);
        }
        
        //统计有多少个集合
        int cnt = 0;
        for (int i = 1, fa; i <= res; i ++) {
            if ((fa = find(i)) != i) continue;
            vis[fa] = vis[fa + res] = ++cnt;
            p[cnt] = s[fa], q[cnt] = s[fa + res];
        }

    要判断我们能否知道每个人的身份的话,也就是我们要凑出来有\(x\)个人是好人,剩下的\(y\)个人是坏人,我们提前已经将所有的人都分成了两个集合,好人集合和坏人集合,那么考虑用\(dp\)来将所有的状态来进行转移,类比于背包求解方案数的问题.定义\(dp[i][j]\)表示的是前\(i\)个连通块中有\(j\)个好人的方案是否存在,如果\(dp[i][j] == 1\)那就是这个方案可行,否则不然.

        dp[0][0] = 1;
        for (int i = 1; i <= cnt; i++) {
            for (int j = std::min(p[i], q[i]); j <= x; j++) {
                if (j >= p[i]) dp[i][j] += dp[i - 1][j - p[i]];
                if (j >= q[i]) dp[i][j] += dp[i - 1][j - q[i]];
            }
        }
        
        if (dp[cnt][x] != 1) {
            std::cout << "no\n";
            continue;
        }
        
        for (int i = cnt; i; i--) {
            if (dp[i - 1][x - p[i]]) x -= p[i], p[i] = -1;
            else if (dp[i - 1][x - q[i]]) x -= q[i], p[i] = -2;
        }

    最后输出方案中的所有的好人

        dp[0][0] = 1;
        for (int i = 1; i <= cnt; i++) {
            for (int j = std::min(p[i], q[i]); j <= x; j++) {
                if (j >= p[i]) dp[i][j] += dp[i - 1][j - p[i]];
                if (j >= q[i]) dp[i][j] += dp[i - 1][j - q[i]];
            }
        }
        
        if (dp[cnt][x] != 1) {
            std::cout << "no\n";
            continue;
        }
        
        for (int i = cnt; i; i--) {
            if (dp[i - 1][x - p[i]]) x -= p[i], p[i] = -1;
            else if (dp[i - 1][x - q[i]]) x -= q[i], p[i] = -2;
        }

    多组测试注意初始化

        int res = x + y;
        memset(dp, 0, sizeof dp);
        for (int i = 1; i <= res; i++) {
            f[i] = i, f[i + res] = i + res;
            dep[i] = dep[i + res] = s[i] = 1;
            s[i + res] = vis[i] = vis[i + res] = 0;
        }
        for (int i = 1; i <= res; i++) s[i] = 1;

标签:dep,int,res,查集,merge,种类,骗子,集合,dp
来源: https://www.cnblogs.com/Haven-/p/16633624.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有