poj 1417 并查集+dp
转自:点我
题目:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。
其中比较重要的是,好人总说真话,坏人总说假话。不需要判断矛盾。唯一解
http://poj.org/problem?id=1417
其中好人说真话,坏人说假话这点很重要。
那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。
如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。
也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。
用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。这是一个经典的并查集问题。
这样处理之后,还需要判断是否唯一
我们通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,我们只有相对关系)
接下来就是从所有大集合中的两个小集合取一个,组成好人集合,判断是否唯一。
背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种,或者dp[i][j]表示当前好人i个,坏人j个的情况有多少种
如果dp[cnt][p1]!=1说明方案不唯一,或者无解。
如果为1题目还需要输出方案,这点比较纠结。用后一种DP的时候WA了好多次,而这题又卡内存,不能开三维数组,其实可以两次DP解决。
后来采用前者DP,不断从dp[cnt][p1]往前递推,递推的结果也必须是某个前趋状态的dp值为1.
1 #include<iostream> 2 #include<cstdio> 3 #include<map> 4 #include<cstring> 5 #include<cmath> 6 #include<vector> 7 #include<algorithm> 8 #include<set> 9 #include<string> 10 #include<queue> 11 #define inf 1<<30 12 #define M 60005 13 #define N 605 14 #define maxn 300005 15 #define pb(a) push_back(a) 16 #define mem(a,b) memset(a,b,sizeof(a)) 17 using namespace std; 18 int pre[N],r[N]; 19 int p1,p2,p; 20 bool vis[N]; 21 int dp[N][N/2]; 22 int cnt; //最后分为几个集合 23 int a[N][2]; //a[i][0],a[i][1]分别表示把第i个集合分成的两个部分 24 vector<int> b[N][2]; 25 int find(int x) 26 { 27 if(x!=pre[x]) 28 { 29 int f=pre[x]; 30 pre[x]=find(pre[x]); 31 r[x]=r[x]^r[f]; 32 } 33 return pre[x]; 34 } 35 void Init() 36 { 37 for(int i=1; i<=p1+p2; i++) pre[i]=i,r[i]=0; 38 mem(vis,false); 39 cnt=1; 40 mem(a,0); 41 for(int i=0; i<N; i++) 42 { 43 b[i][0].clear(); 44 b[i][1].clear(); 45 } 46 } 47 int main() 48 { 49 while(scanf("%d%d%d",&p,&p1,&p2)!=EOF&&p+p1+p2) 50 { 51 Init(); 52 while(p--) 53 { 54 int u,v; 55 char str[10]; 56 scanf("%d%d%s",&u,&v,str); 57 int k=(str[0]=='n'); 58 int ra=find(u),rb=find(v); 59 if(ra!=rb) 60 { 61 pre[ra]=rb; 62 r[ra]=r[u]^r[v]^k; 63 } 64 } 65 for(int i=1; i<=p1+p2; i++) 66 { 67 if(!vis[i]) 68 { 69 int f=find(i); 70 for(int j=i; j<=p1+p2; j++) 71 { 72 if(find(j)==f) 73 { 74 vis[j]=true; 75 b[cnt][r[j]].pb(j); 76 a[cnt][r[j]]++; 77 } 78 } 79 cnt++; 80 } 81 } 82 mem(dp,0); 83 dp[0][0]=1; 84 for(int i=1; i<cnt; i++) 85 { 86 for(int j=p1; j>=0; j--) 87 { 88 if(j-a[i][0]>=0) 89 dp[i][j]+=dp[i-1][j-a[i][0]]; 90 if(j-a[i][1]>=0) 91 dp[i][j]+=dp[i-1][j-a[i][1]]; 92 } 93 } 94 if(dp[cnt-1][p1]!=1) 95 { 96 printf("no\n"); 97 continue; 98 } 99 else 100 { 101 vector<int>ans; 102 ans.clear(); 103 for(int i=cnt-1; i>=1; i--) 104 { 105 if(p1-a[i][0]>=0&&p2-a[i][1]>=0&&dp[i-1][p1-a[i][0]]==1) 106 { 107 for(int j=0; j<b[i][0].size(); j++) 108 { 109 ans.pb(b[i][0][j]); 110 } 111 p1-=a[i][0]; 112 p2-=a[i][1]; 113 } 114 else if(p1-a[i][1]>=0&&p2-a[i][0]>=0&&dp[i-1][p1-a[i][1]]==1) 115 { 116 for(int j=0; j<b[i][1].size(); j++) 117 { 118 ans.pb(b[i][1][j]); 119 } 120 p1-=a[i][1]; 121 p2-=a[i][0]; 122 } 123 } 124 sort(ans.begin(),ans.end()); 125 for(int i=0; i<ans.size(); i++) printf("%d\n",ans[i]); 126 printf("end\n"); 127 } 128 } 129 return 0; 130 }