wenbao与并查集
并查集:
1 int Find(int x){ 2 if(T[x] == -1) return x; 3 int xx = T[x]; 4 T[x] = Find(xx); 5 sum[x] += sum[xx]; 6 return T[x]; 7 }
http://poj.org/problem?id=1456
题意: 有N件商品,知道了商品的价值和销售的最后期限,只要在最后日期之前销售处,就能得到相应的利润,并且销售该商品需要1天时间,求出最大利润。
暴力做法:
1 #include <iostream> 2 #include <stdio.h> 3 #include <queue> 4 #include <string.h> 5 #include <algorithm> 6 using namespace std; 7 struct Node{ 8 int x, y; 9 }T[10009]; 10 int cmp(Node a, Node b){ 11 if(a.x == b.x) return a.y > b.y; 12 return a.x > b.x; 13 } 14 bool vis[10009]; 15 int main(){ 16 int n, sum; 17 while(~scanf("%d", &n)){ 18 sum = 0; 19 for(int i = 0; i < n; i++){ 20 scanf("%d%d", &T[i].x, &T[i].y); 21 } 22 sort(T, T+n, cmp); 23 memset(vis, false, sizeof(vis)); 24 for(int i = 0; i < n; i++){ 25 for(int j = T[i].y; j >= 1; j--){ 26 if(!vis[j]){ 27 vis[j] = true; 28 sum += T[i].x; 29 break; 30 } 31 } 32 } 33 printf("%d\n", sum); 34 } 35 return 0; 36 }
并查集:
1 #include <iostream> 2 #include <stdio.h> 3 #include <queue> 4 #include <string.h> 5 #include <algorithm> 6 using namespace std; 7 const int maxn = 10009; 8 struct Node{ 9 int x, y; 10 }T[maxn]; 11 int cmp(Node a, Node b){ 12 if(a.x == b.x) return a.y > b.y; 13 return a.x > b.x; 14 } 15 int TT[maxn]; 16 int Find(int x){ 17 return TT[x] == x ? x : TT[x] = Find(TT[x]); 18 } 19 int main(){ 20 int n; 21 while(~scanf("%d", &n)){ 22 int sum = 0; 23 for(int i = 0; i < n; i++){ 24 scanf("%d%d", &T[i].x, &T[i].y); 25 } 26 sort(T, T+n, cmp); 27 for(int i = 0; i < maxn; i++){ 28 TT[i] = i; 29 } 30 for(int i = 0; i < n; i++){ 31 int xx = Find(T[i].y); 32 if(xx > 0){ 33 sum += T[i].x; 34 TT[xx] = xx-1; 35 } 36 } 37 printf("%d\n", sum); 38 } 39 return 0; 40 }
使用并查集后快的不是一点半点(快了一倍多)
总结:并查集牛!!!!!!!!!
带权并查集:
1 T[yy] = xx; 2 sum[yy] = sum[x] + z - sum[y]; 3 4 T[xx] = yy; 5 sum[xx] = sum[y] - z - sum[x]; 6 7 sum[yy] + sum[xx] == sum[x] + z - sum[y] + sum[y] - z - sum[x] == 0;
http://acm.hdu.edu.cn/showproblem.php?pid=3038
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 using namespace std; 5 int T[200009], sum[200009]; 6 int Find(int x){ 7 if(T[x] == -1) return x; 8 int xx = Find(T[x]); 9 sum[x] += sum[T[x]]; 10 return T[x] = xx; 11 } 12 int main(){ 13 int n, m, x, y, z; 14 while(scanf("%d %d", &n, &m) == 2){ 15 memset(T, -1, sizeof(T)); 16 memset(sum, 0, sizeof(sum)); 17 int num = 0; 18 for(int i = 1; i <= m; i++){ 19 scanf("%d %d %d", &x, &y, &z); 20 x = x - 1; 21 int xx = Find(x), yy = Find(y); 22 if(xx == yy){ 23 if(sum[y] - sum[x] != z) 24 num ++; 25 } 26 else{ 27 T[xx] = yy; 28 sum[xx] = sum[y] - z - sum[x]; 29 } 30 } 31 printf("%d\n", num); 32 } 33 return 0; 34 }
并查集经典题目:食物连
https://vjudge.net/contest/66964#problem/E
并查集的思想
找到儿子与父亲、父亲与爷爷的关系进而t推出儿子与爷爷的关系(重点是向量关系图)
1 #include <iostream> 2 #include <stdio.h> 3 using namespace std; 4 const int maxn = 50009; 5 int T[maxn], re[maxn]; 6 int Find(int x){ 7 if(T[x] == x) return x; 8 int xx = T[x]; 9 T[x] = Find(xx); 10 re[x] = (re[x] + re[xx] + 3) %3; 11 return T[x]; 12 } 13 int main(){ 14 int n, m, num = 0; 15 scanf("%d %d", &n, &m); 16 for(int i = 0; i <= n; i++){ 17 T[i] = i, re[i] = 0; 18 } 19 for(int i = 0; i < m; i++){ 20 int d, x, y; 21 scanf("%d %d %d", &d, &x, &y); 22 if(x > n || y > n){ 23 num ++; 24 continue; 25 } 26 int xx = Find(x), yy = Find(y); 27 if(xx == yy){ 28 if(d == 1 && re[x] != re[y]){ 29 num ++; 30 }else if(d == 2 &&(re[x] == re[y] || (re[y] - re[x] + 3) %3 == 2)){ 31 num ++; 32 } 33 }else{ 34 T[yy] = xx; 35 re[yy] = (3 - re[y] + d - 1 + re[x] + 3) %3; 36 } 37 } 38 printf("%d\n", num); 39 return 0; 40 }
唉!!!!!越陷越深,,,,并查集怎么这么难!!
带权并查集加dp(要死了)
https://vjudge.net/contest/66964#problem/F
判断好人坏人;
1 #include <iostream> 2 #include <algorithm> 3 #include <string.h> 4 #include <stdio.h> 5 #include <vector> 6 using namespace std; 7 const int maxn = 700; 8 int T[maxn], re[maxn], a[maxn][2], dp[maxn][maxn/2], fa[maxn][maxn/2]; 9 bool vis[maxn]; 10 vector<int> v[maxn][2]; 11 void init(int x){ 12 for(int i = 0; i <=x; i++){ 13 T[i] = i, re[i] = 0; 14 v[i][0].clear(); 15 v[i][1].clear(); 16 a[i][0] = a[i][1] = 0; 17 } 18 } 19 int Find(int x){ 20 if(T[x] == x) return x; 21 int xx = T[x]; 22 T[x] = Find(xx); 23 re[x] = (re[x] ^ re[xx]); 24 return T[x]; 25 } 26 int main(){ 27 int t, n, m, num, x, y; 28 char str[5]; 29 while(scanf("%d%d%d", &t, &n, &m)){ 30 if(t == 0 && n == 0 && m== 0) break; 31 int sum = n + m; 32 init(sum); 33 for(int i = 0; i < t; i++){ 34 scanf("%d%d%s", &x, &y, &str); 35 int xx = Find(x), yy = Find(y); 36 if(xx != yy){ 37 if(str[0] == 'y') num = 0; 38 else num = 1; 39 T[xx] = yy; 40 re[xx] = (re[x] ^ num ^ re[y]); 41 } 42 } 43 memset(vis, false, sizeof(vis)); 44 num = 1; 45 for(int i = 1; i <= sum; i++) if(!vis[i]){ 46 int xx = Find(i); 47 for(int j = i; j <= sum; j++)if(!vis[j] && xx == Find(j)){ 48 v[num][re[j]].push_back(j); 49 a[num][re[j]] ++; 50 vis[j] = true; 51 } 52 num ++; 53 } 54 memset(dp, 0, sizeof(dp)); 55 dp[0][0] = 1; 56 for(int i = 1; i < num; i++){ 57 for(int j = n; j >= 0; j--){ 58 if(j - a[i][0] >= 0 && dp[i-1][j-a[i][0]]){ 59 dp[i][j] += dp[i-1][j-a[i][0]]; 60 fa[i][j] = j - a[i][0]; 61 } 62 if(j - a[i][1] >= 0 && dp[i-1][j-a[i][1]]){ 63 dp[i][j] += dp[i-1][j-a[i][1]]; 64 fa[i][j] = j - a[i][1]; 65 } 66 } 67 } 68 if(dp[num-1][n] != 1){ 69 puts("no"); 70 }else{ 71 //cout<<num<<endl; 72 int b[maxn], num2 = 0; 73 for(int i = num-1; i >= 1; i--){ 74 int tem = n - fa[i][n]; 75 //cout<<i<<"******"<<n<<"****"<<tem<<endl; 76 if(tem == a[i][0]){ 77 for(int j = 0; j < a[i][0]; j++){ 78 b[num2++] = v[i][0][j]; 79 } 80 }else{ 81 for(int j = 0; j < a[i][1]; j++){ 82 b[num2++] = v[i][1][j]; 83 } 84 } 85 n = fa[i][n]; 86 } 87 sort(b, b+num2); 88 for(int i = 0; i < num2; i++){ 89 printf("%d\n", b[i]); 90 } 91 puts("end"); 92 } 93 } 94 return 0; 95 }
http://poj.org/problem?id=1733
告诉你区间内奇偶数的个数,求第一个说假话的人。。。。。。
暴力带权并查集:
1 #include <iostream> 2 #include <map> 3 #include <stdio.h> 4 using namespace std; 5 map<int, int> T; 6 map<int, int> sum; 7 int Find(int x){ 8 if(T[x] == 0) return x; 9 int xx = T[x]; 10 T[x] = Find(xx); 11 sum[x] = (sum[x] ^ sum[xx]); 12 return T[x]; 13 } 14 int main(){ 15 int n, t, x, y, a; 16 char str[5]; 17 scanf("%d", &n); 18 scanf("%d", &t); 19 for(int i = 1; i <= t; i++){ 20 scanf("%d%d%s", &x, &y, str); 21 if(str[0] == 'e') a = 0; 22 else a = 1; 23 x = x - 1; 24 int xx = Find(x), yy = Find(y); 25 if(xx != yy){ 26 T[xx] = yy; 27 sum[xx] = (sum[x]^sum[y]^a); 28 }else{ 29 if(sum[x]^sum[y] != a){ 30 printf("%d\n", i-1); 31 return 0; 32 } 33 } 34 } 35 printf("%d\n", t); 36 return 0; 37 }
带权并查集加离散化:
1 #include <iostream> 2 #include <algorithm> 3 #include <stdio.h> 4 using namespace std; 5 const int maxn = 5005; 6 int T[maxn*2], sum[maxn*2], a[maxn*2]; 7 int n, t, b, num = 0, len; 8 struct Node{ 9 int x, y; 10 char str[5]; 11 }TT[maxn]; 12 int Bin(int x){ 13 int l = 0, r = len - 1, mid; 14 while(l <= r){ 15 mid = (l + r) >> 1; 16 if(a[mid] == x) return mid; 17 if(a[mid] > x) r = mid; 18 else l = mid + 1; 19 } 20 } 21 int Find(int x){ 22 if(T[x] == x) return x; 23 int xx = T[x]; 24 T[x] = Find(xx); 25 sum[x] = (sum[x] ^ sum[xx]); 26 return T[x]; 27 } 28 int main(){ 29 scanf("%d", &n); 30 scanf("%d", &t); 31 for(int i = 0; i < t; i++){ 32 scanf("%d%d%s", &TT[i].x, &TT[i].y, TT[i].str); 33 TT[i].x = TT[i].x - 1; 34 a[num++] = TT[i].x, a[num++] = TT[i].y; 35 } 36 sort(a, a+num); 37 len = unique(a, a+num) - a; 38 for(int i = 0; i <= len; i++){ 39 T[i] = i, sum[i] = 0; 40 } 41 for(int i = 0; i < t; i++){ 42 int xx = Bin(TT[i].x), yy = Bin(TT[i].y); 43 int xxx = Find(xx), yyy = Find(yy); 44 if(TT[i].str[0] == 'e') b = 0; 45 else b = 1; 46 if(xxx != yyy){ 47 T[xxx] = yyy; 48 sum[xxx] = (sum[xx]^sum[yy]^b); 49 }else{ 50 if(sum[xx]^sum[yy] != b){ 51 printf("%d\n", i); 52 return 0; 53 } 54 } 55 } 56 printf("%d\n", t); 57 return 0; 58 }
离散化后速度快了两倍还多。。。。。厉害了。。。。
只有不断学习才能进步!