poj 1417(并查集与dp的应用)
前置题目:poj1611、poj1182、hdoj3038
有了前面的积累,这道题目自然就想出了运用并查集将它们归为一类。
以及它们之间的关系,主要是在路径压缩的时候发生了改变。这些在poj1182、hdoj3038都可以体会到这一点。
问题的关键是这个测谎仪,要怎么判断对错。
这里看到网上的题解都是采用dp的方法。
dp[i][j]这里i表示前i个集合,好人为j个的方案有几种。
这里我们应该清楚,如果好人的方案超过了一种。就是说,坏人与好人的说法有各所其词的时刻。这时候我们就无法判断,这么多集合方案下面的是否正确。所以无法判断就是假。
感觉没有说明白啊。。。。。。没关系,去题目那里可以体会到。不过,写下这篇东西后,感觉思路清晰了很多。orz
1 #include<stdio.h> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=606; 7 int fa[maxn],r[maxn]; 8 int p1,p2,p,cnt; 9 bool vis[maxn]; 10 int dp[maxn][maxn/2],a[maxn][2]; 11 vector<int> b[maxn][2]; 12 13 int find_fa(int x) ///路径压缩与查找 14 { 15 if(fa[x]==x) return fa[x]; 16 int tmp=fa[x]; 17 fa[x]=find_fa(tmp); 18 r[x]=(r[x]+r[tmp])%2; 19 return fa[x]; 20 } 21 22 void init() ///初始化 23 { 24 for(int i=0;i<=p1+p2;i++){ 25 fa[i]=i; r[i]=0; 26 } 27 cnt=1; 28 memset( vis, false, sizeof vis); 29 memset( a, 0, sizeof a); 30 memset( dp, 0, sizeof dp); 31 dp[0][0]=1; 32 for(int i=0;i<maxn;i++){ 33 b[i][0].clear(); 34 b[i][1].clear(); 35 } 36 } 37 38 int main() 39 { 40 while( ~scanf("%d%d%d",&p,&p1,&p2)&&p+p1+p2){ 41 init(); 42 while(p--){ 43 int u,v; 44 char str[10]; 45 scanf("%d%d%s",&u,&v,str); 46 int k=(str[0]=='n'); 47 int ra=find_fa(u); 48 int rb=find_fa(v); 49 if(ra!=rb){ 50 fa[ra]=rb; 51 r[ra]=(2-r[u]+r[v]+k)%2; 52 } 53 } 54 55 for(int i=1;i<=p1+p2;i++){ 56 if(!vis[i]){ 57 int tmp=find_fa(i); 58 for(int j=i;j<=p1+p2;j++){ 59 if(tmp==find_fa(j)){ 60 vis[j]=true; 61 b[cnt][r[j]].push_back(j); 62 a[cnt][r[j]]++; 63 ///可以清楚地看到这里的a是记录改集合的个数,b记录改集合的元素 64 } 65 } 66 cnt++; 67 } 68 } 69 70 for(int i=1;i<cnt;i++){ 71 for(int j=p1;j>=0;j--){ ///p1开始很容易看到 72 if(j-a[i][0]>=0) 73 dp[i][j]+=dp[i-1][j-a[i][0]]; 74 75 if(j-a[i][1]>=0) 76 dp[i][j]+=dp[i-1][j-a[i][1]]; 77 ///i集合,j个好人的情况。背包的dp 78 } 79 } 80 81 if(dp[cnt-1][p1]!=1){ ///说法有很多种,测谎仪失效啦 82 printf("no\n"); 83 } 84 else{ 85 vector<int>ans; 86 ans.clear(); 87 for(int i=cnt-1;i>=1;i--){ 88 if(p1-a[i][0]>=0&&p2-a[i][1]>=0&&dp[i-1][p1-a[i][0]]==1){ 89 ///判断中可以看出,我们要找说法唯一的情况 90 for(int j=0;j<b[i][0].size();j++){ 91 ans.push_back(b[i][0][j]); 92 } 93 p1-=a[i][0]; 94 p2-=a[i][1]; 95 } 96 else if(p1-a[i][1]>=0&&p2-a[i][0]>=0&&dp[i-1][p1-a[i][1]]==1){ 97 for(int j=0;j<b[i][1].size();j++){ 98 ans.push_back(b[i][1][j]); 99 } 100 p1-=a[i][1]; 101 p2-=a[i][0]; 102 } 103 } 104 sort( ans.begin(),ans.end()); ///输出答案 105 for(int i=0;i<ans.size();i++) 106 printf("%d\n",ans[i]); 107 printf("end\n"); 108 } 109 } 110 return 0; 111 }