POJ—1417
题意: 给你p1个好人和p2个坏人,编号为1-p1+p2,然后给你n种操作
x1 x2 no:x1说x2不是好人
x1 x2 yes:x1说x2是好人
在这里好人说的总是对的,坏人说的总是坏的,然后问你最后能不能唯一确定哪些是好人,并输出,否则输出”no“
思路:首先,我们假设x1是好人,并且有 x1 x2 yes 那么,x2一定也是好人,如果有x1 x2 no 那么x2一定是坏人。如果假设x1是坏人,如果有x1 x2 no 那么 x2 一定是好人, 如果有 x1 x2 yes 那么x2也是坏人。也就是说,只要给出的是yes,那么,x1,x2一定是同一类人,否则,一定不是同一类。(自己推一下其中的关系就会明白)。那么,根据这个关系,我们就可以用带权并查集来做了,令同一类为0,否则为1。但是,只用带权并查集的话,只能分出若干个大的集合,每个大的集合又分成两个小集合,即 好人集合 与 坏人集合。但是,我们并不知道每个大集合中,哪个小集合是好人集合哪个集合是坏人集合。这时,就需要我们用dp来处理了。从每个大集合里面取一个当作好人集合,判断方案是否唯一。dp[i][j]表示,前i个大集合有j个好人时的方案的个数,最后,只需判断 对应大集合个数和好人个数时的方案数是否为1即可,不唯一,说明不能确定。(可以发现 a说b是神 则ab同类 反之则异类 这样可以处理出每个连通块内神或魔各有多少个,然后dp[i][j]代表走完前i个连通块后且有j个神的情况有多少 顺带记录一下路径即可 因为要求答案唯一 直接输出路径即可)
#include <stdio.h>
#include<algorithm>
#include<iostream>
#include <math.h>
#include<queue>
#include<string>
#include <string.h>
#define mod 1000000007
#define INF 0xfffffff
#include<time.h>
using namespace std;
#define MAX 1002
#define lowbit(x) (x&(-x))//寻找x管辖的长度
int n, m, a, b;
int fa[MAX];
int val[MAX];
int flag;
int cal[MAX];
int dp[MAX][MAX], pre[MAX][MAX];
int s1[MAX], s2[MAX];
int b1[MAX], b2[MAX];
int col[MAX][MAX];
int Hash[MAX];
int ans[MAX], pos;
int find(int x)
{
if (x == fa[x])
return x;
int fx = fa[x];
fa[x] = find(fa[x]);
if (val[x])
val[x] = !val[fx];
else
val[x] = val[fx];
return fa[x];
}
int Merge(int x, int y, int c)
{
int fx = find(x);
int fy = find(y);
if (fx == fy)
return 1;
fa[fy] = fx;
if ((val[x] ^ val[y]) == c)//不符合
{
s1[fx] += s2[fy];
s2[fx] += s1[fy];
s1[fy] = s2[fy] = 0;
val[fy] = !val[fy];
}
else
{
s1[fx] += s1[fy];
s2[fx] += s2[fy];
s1[fy] = s2[fy] = 0;
}
return 0;
}
int main()
{
int k;
while (cin >> k >> a >> b && (k || a || b))
{
n = a + b;
for (int i = 1; i <= n; i++)
{
fa[i] = i;
s1[i] = 1;
s2[i] = 0;
val[i] = 0;
}
flag = 0;
for (int i = 1; i <= k; i++)
{
int x, y, c;
char s[5];
scanf("%d%d%s", &x, &y, s);
if (s[0] == 'y') c = 1;
else c = 0;
if (flag) continue;
if (Merge(x, y, c) && (val[x] ^ val[y]) == c)
{
flag = 1;
}
}
int m = 0;
for (int i = 1; i <= n; i++) find(i);
for (int i = 1; i <= n; i++)
if (s1[i] || s2[i])
{
b1[++m] = s1[i];
b2[m] = s2[i];
Hash[i] = m;
// cout<<b1[m]<<" "<<b2[m]<<endl;
}
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= m; i++)//集合
for (int j = n; j >= 0; j--) {//真话人数
if (j - b1[i] >= 0 && dp[i - 1][j - b1[i]])
{
dp[i][j] += dp[i - 1][j - b1[i]];
pre[i][j] = j - b1[i];
col[i][j] = 1;
}
if (j - b2[i] >= 0 && dp[i - 1][j - b2[i]])
{
dp[i][j] += dp[i - 1][j - b2[i]];
pre[i][j] = j - b2[i];
col[i][j] = 2;
}
if (dp[i][j] > 1) dp[i][j] = 2;
}
if (flag || dp[m][a] != 1)
cout << "no" << endl;
else
{
int j = a;
for (int i = m; i >= 1; i--)
{
cal[i] = col[i][j];
j = pre[i][j];
}
for (int i = 1; i <= n; i++)
if ((cal[Hash[find(i)]] == 1 && val[i] == 0) || (cal[Hash[find(i)]] == 2 && val[i] == 1))
{
cout << i << endl;
}
cout << "end" << endl;
}
}
return 0;
}