F - True Liars - poj1417(背包+并查集)

题意:有这么一群人,一群好人,和一群坏人,好人永远会说实话,坏人永远说假话,现在给你一组对话和好人与坏人的数目P1, P2。

数据里面的no是A说B是坏人, yes代表A说B是好人,就是这样,问题能不能从这些话里面得出来唯一的解,就是可以确定谁是好人谁是坏人,如果不能输出no,如果可以输出所有的好人。

分析,可以把这些人依据他们中的关系分成几个小集合,集合里面保存两类人的数目,然后DP求出来好人或者坏人是不是唯一的即可,‘

注意并查集时候可以这么认为凡是yes都是同类,no都是异类

///////////////////////////////////////////////////////////////

#include<iostream>

#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<queue>
using namespace std;

const int maxn = 605;

struct people
{
    int father, relation;
    int same, other;//同类数目,和异类数目
    int True;//是说谎者还是诚实者
}p[maxn];

int dp[maxn][maxn];

int Find(int x)
{
    int k = p[x].father;
    if( p[x].father != x )
    {
        p[x].father = Find(k);
        p[x].relation = (p[x].relation+p[k].relation)%2;
    }

return p[x].father;
}

int main()
{
    int M, T, L;

while(scanf("%d%d%d", &M, &T, &L), M+T+L)
    {
        int i, j, u, v, ru, rv, ok=0, d, N=T+L, k=1;
        char s[10];int f[maxn];//f记录最后小集体的头结点

for(i=0; i<=N; i++)
        {
            p[i].father = i;
            p[i].other = 0;
            p[i].same = 1;//自己和自己是同类,所以最少也有一个
            p[i].relation = 0;
            p[i].True = 0;
        }

while(M--)
        {
            scanf("%d%d%s", &u, &v, s);

if(ok)continue;
            ru = Find(u), rv = Find(v);

if(s[0] == ‘y‘)
                d = 0;//0表示同类,1表示异类
            else d = 1;

if(ru == rv && (p[v].relation+p[u].relation)%2 != d)
                ok = 1;
            else if(ru != rv)
            {
                p[ru].father = rv;
                p[ru].relation = (p[u].relation+p[v].relation+d)%2;
            }
        }

if(!ok)//有可能说的话有矛盾
        {
            for(i=1; i<=N; i++)
            {
                u = Find(i);
                if(u == i)
                    f[k++] = i;
                else
                {
                    p[u].other += p[i].relation;
                    p[u].same += 1-p[i].relation;
                }
            }

memset(dp, 0, sizeof(dp));

dp[1][ p[ f[1] ].same ] += 1;
            dp[1][ p[ f[1] ].other ] += 1;

for(i=2; i<k; i++)
            {
                u = f[i];
                for(j=0; j<=N; j++)
                {
                    if(dp[i-1][j])
                    {
                        dp[i][ p[u].same+j ] += dp[i-1][j];
                        dp[i][ p[u].other+j ] += dp[i-1][j];
                    }
                }
            }
        }

if(dp[k-1][L] != 1 || ok)
            printf("no\n");
        else
        {
            for(i=k-1; i>0; i--)
            {
                u = f[i];
                v = p[u].same;
                if( (i!=1 && dp[i-1][T-v] != 0) || (i==1 && T==v) )
                {
                    p[u].True = 1;
                    T -= v;
                }
                else
                    T -= p[u].other;
            }

for(i=1; i<=N; i++)
            {
                u = p[i].father;
                if(p[u].True && !p[i].relation || p[u].True==0 && p[i].relation)
                    printf("%d\n", i);
            }
            printf("end\n");
        }
    }

return 0;
}
/*
1 1 1
1 2 yes

*/

时间: 2024-08-25 14:33:57

F - True Liars - poj1417(背包+并查集)的相关文章

F - True Liars 带权并查集

After having drifted about in a small boat for a couple of days, Akira Crusoe Maeda was finally cast ashore on a foggy island. Though he was exhausted and despaired, he was still fortunate to remember a legend of the foggy island, which he had heard

#383 Div1 Problem B Arpa&#39;s weak amphitheater.... (分组背包 &amp;&amp; 并查集)

题意 : 有n个人,每个人都有颜值bi与体重wi.剧场的容量为W.有m条关系,xi与yi表示xi和yi是好朋友,在一个小组. 每个小组要么全部参加舞会,要么参加人数不能超过1人. 问保证总重量不超过W,剧场中的颜值最大能到多少? 分析 : 很显然的分组背包题目, 不过有所不同, 先来回顾一下普通的分组背包的描述 给出一个背包,背包有容量C,再给出N组物品,每组物品有ki种,每种物品有对应的体积Vi,价值Pi,每组物品至多选一种,且最多取一件.求用背包装物品,能获得的最大总价值是多少.可以发现和上

Codeforces 1166 F. Vicky&#39;s Delivery Service 并查集+set

题意:有n个点,m条边,边有c种颜色,q次操作. 每个边都有一种颜色. 然后操作有两种,一种是再加一条边,另一种是查询能否从x达到y. 移动的限制是,连着走两步必须是同一种颜色,如果走奇数步,最后一步可以是任意颜色. 例子:1-2-3-4-5-6. 这个题颜色种类很多,我是用map<int,vector<int>>来存边. 我们首先可以想到 对于点x同种颜色连着的点都是可以相互移动的,所以我们可以用这种方法将它们用并查集合并,所以我们也可以直接map<int,int>来

GYM 101173 F.Free Figurines(贪心||并查集)

原题链接 题意:俄罗斯套娃,给出一个初始状态和终止状态,问至少需要多少步操作才能实现状态转化 贪心做法如果完全拆掉再重装,答案是p[i]和q[i]中不为0的值的个数.现在要求寻找最小步数,显然要减去一些多余的步数.如果初始的一些链的前端是终止的某一条链的连续的一部分,那么这条链就不用被拆开再连上,这样每一个长度为x的链对答案的贡献就是-2*(x-1),对每条链进行同样的操作之后就是答案 #include <iostream> #include<cstdio> #include<

vijos 1250 最勇敢的机器人 分组背包+并查集

P1250最勇敢的机器人 背景 Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了~ 描述 机器人们都想知道谁是最勇敢的,于是它们比赛搬运一些物品. 它们到了一个仓库,里面有n个物品,每个物品都有一个价值Pi和重量Wi,但是有些物品放在一起会爆炸,并且爆炸具有传递性.(a和b会爆炸.b和c会爆炸则a和c会爆炸) 机器人们可不想因此损失自己好不容易从Wind那里敲诈来的装备,于是它们想知道在能力范围内,它们最多可以拿多少价值的物品. 你能帮助它们吗? 格式 输入格式 每组测

poj1417(种类并查集+dp)

题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示x说y是天使,ch为no表示x说y不是天使(x, y为天使,恶魔的编号,1<=x,y<=p+q):天使只说真话,恶魔只说假话: 如果不能确定所有天使的编号,输出no,若能确定,输出所有天使的编号,并且以end结尾: 注意:可能会有连续两行一样的输入:还有,若x==y,x为天使: 思路:种类并查集+

F - Wireless Network 暴力更新+并查集

#include <cstdio> #include <iostream> #include <cmath> #include <cstdlib> #include <cstring> #define _for(i,a,b) for(int i= a;i<b;i++) using namespace std; const int N = 1e3+5; typedef long long ll; int flag[N],f[N]; int n

[POJ 2588]--Snakes(并查集)

题目链接:http://poj.org/problem?id=2588 Snakes Time Limit: 1000MS   Memory Limit: 65536K   Description Buffalo Bill wishes to cross a 1000x1000 square field. A number of snakes are on the field at various positions, and each snake can strike a particular

【bzoj3376-方块游戏】带权并查集

题意: n块积木,m个操作或询问.每次移动积木的时候,约翰会选择两块积木X,Y,把X搬到Y的上方.如果X已经和其它积木叠在一起了,那么应将这叠积木整体移动到Y的上方:如果Y已经和其它积木叠在一起了的,假设在Y上方最高处的积木为Z,那么应将X所在的那叠积木移动到Z的上方.每次询问当前时刻,某一块积木的下方有多少块积木.n,m<=10^5 题解: 带权并查集.对于每个点x,维护当前所在并查集(也就是这一堆积木中)最下方的积木d[x],最上方的积木fa[x],x到最上方积木的距离f[x],则下方的积木