POJ1417 True Liars
题意&思路:
一个写了三天的题。
一个岛上存在p1个好人和p2个坏人。Akira可以询问n个问题,每个问题由两个编号x,y和一个字符串a(yes或者no)组成。好人会说实话,坏人会说假话。每次询问表示x如果想让你认为y是好人就会回答yes。问是否能够得到好人的编号。
加权并查集+dp。
加权并查集并不难想,我们不难发现回答yes的是同类,no的是异类。我们用权值0表示同类,1表示异类,每次区间合并和路径压缩的时候可以进行取模运算,也可以考虑异或运算。
这样我们就把岛上的人分成了多个集合,每个集合包含两种人。这时候我们采用背包问题的解法。因为每个集合都必须选一类人,所以我们要用二维数组(用一维一直卡,因为一维不能表示用了多少组,所以不能确定有没有选某个集合)。
状态转移方程为dp[i][j]+=dp[i-1][j-a[i][0/1]]。i表示选了几组,j表示选了多少个人,dp的值为方案数。
最后看dp[tot][p1]是否等于1,tot为集合的总数。当且仅当方案数为1的时候才能确定编号。
编号的输出(又卡了好长时间),用倒推的方式,因为dp[tot][p1]的方案数为1,所以能使之成立的dp[i-1][p1-a[i][0/1]]的方案数也只能为1。
然后排序输出就好了。可是鬼迷心窍的多输出了yes。
以及初始化,初始化,初始化。
代码:
#include<map>
#define ll long long
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<ctype.h>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
const int N=1e3+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
int fa[N],sum[N],dp[N][N],a[N][2],ans[N];
vector<int> son[N][2];
map<int,int> maze;
int find(int x)
{
if(x!=fa[x])
{
int t=fa[x];
fa[x]=find(fa[x]);
sum[x]^=sum[t];
}
return fa[x];
}
void add(int u,int v,int w)
{
int t1=find(u),t2=find(v);
if(t1!=t2)
{
fa[t1]=t2;
sum[t1]=sum[u]^sum[v]^w;
}
return;
}
int main()
{
int n,p1,p2;
while(~scanf("%d%d%d",&n,&p1,&p2) && n+p1+p2)
{
int i,j;
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
maze.clear();
for(i=1;i<=p1+p2;i++)
{
fa[i]=i;
sum[i]=0;
for(j=0;j<=1;j++)
son[i][j].clear();
}
while(n--)
{
int x,y,w;
char s[5];
scanf("%d%d%s",&x,&y,s);
if(x==y) continue;
if(!strcmp(s,"yes"))
w=0;
else
w=1;
add(x,y,w);
}
int tot=0;
for(i=1;i<=p1+p2;i++)
{
int t=find(i);
if(maze[t]==0) maze[t]=++tot;
a[maze[t]][sum[t]^sum[i]]++;
son[maze[t]][sum[t]^sum[i]].push_back(i);
}
dp[0][0]=1;
for(i=1;i<=tot;i++)
for(j=p1;j>=0;j--)
{
if(j>=a[i][0])
dp[i][j]+=dp[i-1][j-a[i][0]];
if(j>=a[i][1])
dp[i][j]+=dp[i-1][j-a[i][1]];
}
int l=p1,len=0;
if(dp[tot][p1]==1)
{
for(i=p1+p2;i>=1;i--)
if(dp[i-1][l-a[i][0]]==1)
{
l-=a[i][0];
for(j=0;j<son[i][0].size();j++)
ans[++len]=son[i][0][j];
}
else if(dp[i-1][l-a[i][1]]==1)
{
l-=a[i][1];
for(j=0;j<son[i][1].size();j++)
ans[++len]=son[i][1][j];
}
sort(ans+1,ans+len+1);
for(i=1;i<=len;i++)
printf("%d\n",ans[i]);
printf("end\n");
}
else
printf("no\n");
}
return 0;
}