解题思路:并查集+dp
分析易知:如果答案为"no",则xi,yi属于两种不同类型(1),否则为同一类型(0)
a)并查集的思想类似于POJ 1733 Parity Game relation[i]表示节点i与根节点关系
b)此时,我们将所有人分成了几个(s)不同的集合,每个集合分为两类:与根节点同类的,与根节点不同类的。
c)题目转化为求解是否存在唯一解:取每个集合中的一种类型(必须且仅取一种),累计人数为p1
题目转化为动态规划问题,dp[i][j]表示节点i(包括i)之前,人数不超过j的最大人数
node[i].s表示与该节点同类型的节点个数,node[i].d表示与该节点类型不同的节点个数
dp[i][j] = max{dp[i - 1 ][j - node[i].s] + node[i].s, dp[i - 1 ][j - node[i].d] + node[i].d}
为了保证(必须且仅取一种)条件,必须保证dp公式中 dp[i-1][j-node[i].s],dp[i-1][j-node[i].s](i>1)不为0,否则该项用0代替
d)为了保证解唯一,如果dp[i-1][j-node[i].s]==dp[i-1][j-node[i].s],我们可以立即判断不满足条件(该过程仅仅是优化而已,如果不判断,下面过程同样可以判断出解不唯一)
e)如果dp[s][pi]==pi,则说明存在解,为了保证解唯一,(s,pi)到达(0,0)应该只有唯一的路径
dp[i][t] - node[i].s == dp[i - 1 ][t - node[i].s]
dp[i][t] - node[i].d == dp[i - 1 ][t - node[i].d]
分别表示选取同类与不同类两种情况,我们必须满足二者仅有一个等式满足
#include < iostream >
using namespace std;
#define MAX 601
int root[MAX],same[MAX],diff[MAX],_map[MAX];
int dp[MAX][MAX];
char relation[MAX];
struct {
int s, d, c;
}node[MAX];
int findRoot( int x)
{
int t;
if (x != root[x]){t = root[x];root[x] = findRoot(root[x]);relation[x] ^= relation[t];}
return root[x];
}
int main()
{
int i,j, n, p1, p2, a, b, v, r1, r2, s, a1, a2, t;
char ch[ 4 ];
bool flag, flag1, flag2;
while (scanf( " %d %d %d " , & n, & p1, & p2) && (n + p1 + p2))
{
flag = true ;
if (p1 == p2)flag = false ;
for (i = 0 ; i < MAX; i ++ )root[i] = i,same[i] = diff[i] = relation[i] = _map[i] = 0 ;
memset(dp, 0 , sizeof (dp));
for (i = 0 ; i < n; i ++ )
{
scanf( " %d %d %s " , & a, & b, ch);
if ( ! flag) continue ;
v = (ch[ 0 ] == ' y ' ) ? 0 : 1 ;
r1 = findRoot(a), r2 = findRoot(b);
if (r1 != r2)
{
if (r1 < r2){root[r2] = r1; relation[r2] = relation[a] ^ relation[b] ^ v;}
else {root[r1] = r2; relation[r1] = relation[a] ^ relation[b] ^ v;}
}
}
if ( ! flag){printf( " no\n " ); continue ;}
for (i = 1 ; i <= (p1 + p2); i ++ )
t = findRoot(i),(relation[i] == 0 ) ? (same[t] ++ ) : (diff[t] ++ );
for (s = 0 ,i = 1 ;i <= (p1 + p2);i ++ )
if (same[i] > 0 ){_map[i] = s;node[s].s = same[i];node[s ++ ].d = diff[i];}
a1 = min(node[ 0 ].d, node[ 0 ].s),a2 = max(node[ 0 ].d, node[ 0 ].s);
if (a1 == a2){printf( " no\n " ); continue ;}
for (i = a1;i < a2;i ++ )dp[ 0 ][i] = a1;
for (i = a2;i <= p1;i ++ )dp[ 0 ][i] = a2;
for (i = 1 ;i < s && flag;i ++ )
{
a1 = min(node[i].s, node[i].d),a2 = max(node[i].s, node[i].d);
if (a1 == a2){flag = false ; break ;}
for (j = a1;j <= p1;j ++ )
{
dp[i][j] = dp[i - 1 ][j - a1] ? (dp[i - 1 ][j - a1] + a1): 0 ;
if (j - a2 > 0 && dp[i - 1 ][j - a2])
{
t = dp[i - 1 ][j - a2] + a2;
if (t > dp[i][j])dp[i][j] = t;
}
}
}
if (dp[s - 1 ][p1] != p1)flag = false ;
for (i = s - 1 ,t = p1;i > 0 && flag;i -- )
{
flag1 = flag2 = false ;
if (t > node[i].s && dp[i][t] - node[i].s == dp[i - 1 ][t - node[i].s])
flag1 = true ;
if (t > node[i].d && dp[i][t] - node[i].d == dp[i - 1 ][t - node[i].d])
flag2 = true ;
if (flag1 && flag2)flag = false ;
if (flag1){node[i].c = 0 ; t -= node[i].s;}
else {node[i].c = 1 ; t -= node[i].d;}
}
if (node[ 0 ].s == t)node[ 0 ].c = 0 ;
else node[ 0 ].c = 1 ;
if (flag)
{
for (i = 1 ;i <= (p1 + p2);i ++ )
if (node[_map[root[i]]].c == relation[i])printf( " %d\n " , i);
printf( " end\n " );
}
else printf( " no\n " );
}
return 0 ;
}