POJ 1417 True Liars(路径压缩并查集+DP背包问题)
来源:互联网 发布:nba2konline强退软件 编辑:程序博客网 时间:2024/05/13 02:41
POJ 1417 True Liars(路径压缩并查集+DP背包问题)
http://poj.org/problem?id=1417
题意:
给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。
其中比较重要的是,好人总说真话,坏人总说假话(这句话的特殊意义)。不会存在矛盾情况。请问你是否存在唯一解,如果存在请输出唯一解。
分析:
如果A说B是好人,那么A与B是同一类人。如果A说B是坏人,那么A与B不同类。(想想为什么是这样)
所以原题所给的每句话就是一条路径压缩并查集的关系,那么我们最终合并所有关系可以得到cnt个连通分量且每个分量中节点的相互关系(同类or异类)我们都知道。
好,现在假设有cnt个连通分量,第一个分量有x1与y1个两类人(我们不知道到底x1那些人是好人还是y1那些人是好人),第二个分量有x2与y2个两类人...所以我们现在想知道是否只有一种方式让我们从第一分量中抓X1(或Y1)个人来,从第二分量中抓X2(或Y2)人来...直到每个分量都抓一类人时,正好抓了p1个人。
如果只有1种方式实现上面的目的,那么就有唯一解。记录DP的每次选择最后输出即可。
令d[i][j]表示取完前i个分量后正好j人的方法数,我们最后要求的是看d[cnt][p1]是否==1?
DP转移方程:dp[i][j]=sum{ dp[i-1][j-bag[i][0]]+dp[i-1][j-bag[i][1]] }
AC代码:
<span style="font-size:18px;">#include<cstdio>#include<cstring>#include<map>using namespace std;const int MAXN=1000;int bag[MAXN][2];//bag[i][0]表示第i(重新编号了)个连通分量的0类人的个数,bag[i][1]表1类人map<int,int> mp;//用来将老的连通分量编号映射bag中的新编号int cnt;//一共有cnt个分量int insert(int x,int b)//连通分量x,与x的关系是b(0表示同类,1表示异类){ if(mp.find(x)==mp.end())mp[x]=++cnt; bag[mp[x]][b]++;//该分量的b类人加1 return mp[x];}int F[MAXN];int v[MAXN];//表示i与根的关系int findset(int i){ if(F[i]==-1)return i; int temp= findset(F[i]); v[i] =(v[i]+v[F[i]])%2; return F[i]=temp;}void bind(int i,int j,int temp){ int fa=findset(i); int fb=findset(j); if(fa!=fb) { F[fb]=fa; v[fb]=(v[i]+v[j]+temp)%2; }}int d[MAXN][310];//DPint main(){ int n,p1,p2; while(scanf("%d%d%d",&n,&p1,&p2)==3) { if(n==0&&p1==0&&p2==0)break; cnt=0; mp.clear(); memset(bag,0,sizeof(bag)); memset(F,-1,sizeof(F)); memset(v,0,sizeof(v)); memset(d,0,sizeof(d)); while(n--) { int a,b,temp; char str[10]; scanf("%d%d%s",&a,&b,str); if(str[0]=='y') temp=0; else if(str[0]=='n') temp=1; int fa=findset(a); int fb=findset(b); if(fa!=fb)//不同分量 bind(a,b,temp);; } for(int i=1;i<=p1+p2;i++)//将1到p1+p2所有点重新编号 { int fi=findset(i); insert(fi,v[i]); } d[0][0]=1;//初值 for(int i=1;i<=cnt;i++)//连通分量编号从1到cnt { for(int j=0;j<=p1;j++) { if( j>=bag[i][0] ) d[i][j] = d[i-1][j-bag[i][0]]; if( j>=bag[i][1] ) d[i][j] += d[i-1][ j-bag[i][1] ]; } } //printf("###%d\n",d[cnt][p1]); if(d[cnt][p1]==1)//能区分出 { int j=p1; int choose[MAXN];//choose[i]=1/0表示第i(重新编号)个连通分量选择第0类还是选第1类 memset(choose,-1,sizeof(choose)); for(int k=cnt;k>=1;k--)//逆推找出choose { if( d[k][j] == d[k-1][j-bag[k][0]] ) { choose[k]=0; j=j-bag[k][0]; } else if( d[k][j] == d[k-1][j-bag[k][1]] ) { choose[k]=1; j=j-bag[k][1]; } } for(int i=1;i<=p1+p2;i++) { int fa=findset(i);//找出分量的编号fa int num=mp[fa];//找出fa重新编号后的编号 num if(v[i]==choose[num]) printf("%d\n",i); } printf("end\n"); } else { printf("no\n"); } } return 0;}</span>
- POJ 1417 True Liars(路径压缩并查集+DP背包问题)
- POJ--1417[True Liars] 并查集+背包
- poj 1417 - True Liars(并查集+背包)
- poj 1417 True Liars 解题报告 并查集 DP
- POJ 1417 True Liars(并查集+DP)
- poj 1417 True Liars (并查集+dp)
- POJ 1417 True Liars(并查集+DP)
- poj-1417 True Liars 并查集+DP
- poj 1417 True Liars(并查集+DP)
- poj -- 1417 True Liars(并查集 + dp)
- POJ 1417 True Liars(并查集+DP)
- POJ 1417 True Liars 带权并查集+DP
- POJ-1417-并查集-True Liars
- 【POJ1417】【True Liars】【加权并查集+背包+输出路径】
- 并查集+dp True Liars
- poj 1417 True Liars 带权并查集+母函数dp
- POJ1417 True Liars (并查集+背包)
- True Liars (并查集+dp,待续、、)
- python 合法输入的判断
- python 把列表转为字串
- 把数字转为二进制的字串
- 反省
- 麻省理工的 背包算法 python
- POJ 1417 True Liars(路径压缩并查集+DP背包问题)
- MIT 背包问题的进一步理解
- 随机走动的模拟
- pylab
- pylab的环境配置
- 大话数据结构第一个例子的计算比较
- 科普文:自然语言处理的两条道路
- python 算法之栈
- 队列与约瑟夫问题