这个题做了两天了。首先用并查集分类是明白的, 不过判断是否情况唯一刚开始用的是搜索。总是超时。 后来看别人的结题报告, 才恍然大悟判断唯一得用DP.

题目大意:

一共有p1+p2个人,分成两组,一组p1个,一组p2个。给出N个条件,格式如下:

x y yes表示x和y分到同一组

x y no表示x和y分到不同组

问分组情况是否唯一,若唯一则按从小到大顺序输出,否则输出no。保证不存在矛盾条件,但是有可能出现x=y的情况。

题目分析: 题中会给我们一些信息, 告诉我们那些是同一类, 哪些是不同类。 当然刚开始的时候我们无法判断那一类是好人、坏人。 那么我们不妨把有关系的点(无论他们的关系是yes还是 no)全归为一类, 他们有一个相同的父节点。然后用一个数组(relation[])记录他与父节点的关系(0代表同类, 1代表异类)。当然因为所给的只是一部分信息, 所以有可能无法把所有点归为一类(例如:1,2 yes 3,4 yes。只表明1,2同类 , 3,4同类, 1,3的关系并不知道)。那么不妨设几个不同的集合(以父节点为划分标准)每个集合分为两类 ,与父节点同类(relation[] = 0), 与父节点不同类(relation[] = 1)。此时我们问题转变为答案是否唯一。取每个集合中的一种类型,且仅取一种(同类或不同类)。看累计人数得p1的情况是否唯一。

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std; int n, n1, n2, mi, mx, key, flag, a[][], vis[][][], ans[][], v[], d[][], pre[], relation[];
int find(int x)//寻找最根部的父亲节点
{
if(pre[x] == x)
return x;
else if(pre[x] != x)
{
int t = pre[x];
pre[x] = find(pre[x]);//边寻找最根部父亲节点,边更新原父亲节点的信息
relation[x] = (relation[t] + relation[x]) % ;//类似于种类并查集的更新
}
return pre[x];
}
void work(int a, int b, int c)//合并节点
{
int fx = find(a);
int fy = find(b);
if(fx != fy)
{
pre[fx] = fy;
relation[fx] = (relation[a] + relation[b] + c) % ;
}
} void dp()
{
int k = ;
for(int i = mi+; i <= (n1+n2); i++)
{
if(v[i] == )
{
k++;
int t1 = ans[i][];
int t2 = ans[i][];
int mx = min(t1, t2);
for(int j = n1; j >= mx; j--)
{
d[k][j] = d[k-][j-t1] + d[k-][j-t2];
if(d[k-][j-t1] == && d[k-][j-t2] == )
{
vis[k][j][] = i;
vis[k][j][] = ;
}
else if(d[k-][j-t2] == && d[k-][j-t1] == )
{
vis[k][j][] = i;
vis[k][j][] = ;
}
}
}
}
}
int main()
{
while(scanf("%d%d%d", &n, &n1, &n2) != EOF)
{
if(n == && n1 == && n2 == )
break;
//初始化所有节点得父节点和relation
for(int i = ; i <= n1+n2; i++){pre[i] = i; relation[i] = ;}
for(int i = ; i <= n; i++)
{
int a, b;
char c[];
scanf("%d%d%s", &a, &b, &c);
if(strcmp(c, "yes") == )
work(a, b, );
else if(strcmp(c, "no") == )
work(a, b, );
}
//这里注意一下:到这一步,有可能存在一些点的父节点不是最跟的节点
for(int i = ; i <= n1+n2; i++)
int t = find(i);
memset(v, , sizeof(v));
//ans[i][0]表示与最根节点i同类的节点个数, ans[i][1]代表与最根节点i不同类的节点个数
memset(ans, , sizeof(ans));
flag = ;//存储一共有多少个不同的最跟部的父节点
mi = 10e9;
for(int i = ; i <= n1+n2; i++)
{
int x = pre[i];
int y = relation[i];
if(x < mi)
mi = x;
ans[x][y]++;
if(v[x] == )
{
v[x] = ;
flag++;
}
}
/*d[i][j]前i个集合中累计人数为j时有多少种可能, vis[i][j][]保存每一个集合选了哪一类, 为最后输出用。 vis[i][j][0]表示前i个集合中累计人数为j时的最根节点,vis[i][j][1] 表示前i个集合中累计人数为j时,最根节点为vis[i][j][0]时,与根节点的关系*/
memset(d, , sizeof(d));
memset(vis, , sizeof(vis));
d[][ans[mi][]]++; d[][ans[mi][]]++;
vis[][ans[mi][]][] = mi;
vis[][ans[mi][]][] = ;
vis[][ans[mi][]][] = mi;
vis[][ans[mi][]][] = ;
dp();
if(d[flag][n1] == )//如果前flag个集合中累计人数为n1的可能为1时,有唯一解
{
int j = n1;
for(int i = flag; i >= ; i--)//从后往前推
{
a[i][] = vis[i][j][];
a[i][] = vis[i][j][];//记录第i个集合中取得是哪一类(同类0, 不同类1)
j -= ans[a[i][]][a[i][]];
}
for(int i = ; i <= n1+n2; i++)
{
for(int j = ; j <= flag; j++)
{
int f = pre[i];
int ff = relation[i];
if(f == a[j][] && ff == a[j][])
printf("%d\n", i);
}
}
printf("end\n");
}
else
printf("no\n");
}
return ;
}

poj1417 true liars(并查集 + DP)详解的更多相关文章

  1. POJ1417 True Liars —— 并查集 + DP

    题目链接:http://poj.org/problem?id=1417 True Liars Time Limit: 1000MS   Memory Limit: 10000K Total Submi ...

  2. POJ1417 True Liars 并查集 动态规划 (种类并查集)

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ1417 题意概括 有一群人,p1个好人,p2个坏人. 他们说了n句话.(p1+p2<=600,n ...

  3. poj1417 True Liars[并查集+背包]

    有一点小转化的题,在设计dp状态时还是有点费脑筋的. 地址. 依题意,首先可以知道肯定要扩展域的并查集(明摆着的嘛).一个"好人"域,一个"坏人"域,每句话分两 ...

  4. poj1308 Is It A Tree?(并查集)详解

    poj1308   http://poj.org/problem?id=1308 题目大意:输入若干组测试数据,输入 (-1 -1) 时输入结束.每组测试数据以输入(0 0)为结束标志.然后根据所给的 ...

  5. poj1182 食物链(种类并查集)详解

    poj 1182   http://poj.org/problem?id=1182 分析:这个题大意说的非常清楚了,就是求出假话的个数,题目中给的假话要求有三个 ① 当前的话与前面的某些真的话冲突,是 ...

  6. poj1417(种类并查集+dp)

    题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示 ...

  7. 数位DP 详解

    序 天堂在左,战士向右 引言 数位DP在竞赛中的出现几率极低,但是如果不会数位DP,一旦考到就只能暴力骗分. 以下是数位DP详解,涉及到的例题有: [HDU2089]不要62 [HDU3652]B-n ...

  8. POJ1417:True Liars(DP+带权并查集)

    True Liars Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. 【POJ1417】【带标记并查集+DP】True Liars

    Description After having drifted about in a small boat for a couple of days, Akira Crusoe Maeda was ...

随机推荐

  1. C#实现调用Java类中方法

    基本思路: 用C#实现调用Java编写的类中的方法:重点是将Java编写的程序打包成Jar,然后使用开源工具IKVM将其转化成DLL控件,在.NET环境下调用. 分为以下步骤: 1.下载JDK6(注: ...

  2. 一步步写STM32 OS【一】 序言

    一直想写个类似uCOS的OS,近段时间考研复习之余忙里偷闲,总算有点成果了.言归正传,我觉得OS最难的部分首先便是上下文切换的问题,他和MCU的架构有关,所以对于不同的MCU,这部分需要移植.一旦这个 ...

  3. 【转】Installing OpenCV on Debian Linux

    In this post I will describe the process of installing OpenCV(both versions 2.4.2 and 2.4.3) on Debi ...

  4. [codevs3862]竞赛班的垃圾处理

    题目描述 Description 竞赛班的师弟们都很懒,每个人都不喜欢倒垃圾,结果垃圾就会跟师兄的RP一样越堆越多,当然,老师时不时就会叫他们去倒垃圾.由于他们真的很懒,他们只会直接将垃圾桶最上面袋拿 ...

  5. 杨氏矩阵 leecode 提

    提交网址https://oj.leetcode.com/problems/search-a-2d-matrix/ 有个矩阵中的数,从左向右递增,从上而下递增,快速查找是一个数是是否存在,剑指offer ...

  6. bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1807  Solved: 772[Submit][Stat ...

  7. HW5.6

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  8. HW3.14

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  9. Python模块导入及使用经验回顾 [ 持续更新 ]

    1,若需要导入的模块并不是一个简单的*.py文件,而是在Lib目录下的一个文件夹,则要注意检查这个文件夹下有无__init__.py文件(该文件虽然经常为空,但是缺失该文件,对模块的导入有很大的影响) ...

  10. Kooboo CMS 介绍

    Kooboo的定位是一个CMS,内容管理平台,从更严格意义上来说,它更应该网站快速开发平台.针对一般网站开发过程的分析和提炼,着重在解决网站的一般需求,提出一套快速开发网站的理念和方法.在这些理念和方 ...