1、
Wireless Network
题意:有n台电脑,距离d以内的两台电脑维修后可以互相联系。一台电脑可以通过一对能够互相联系的电脑中的一个与另一个联系。给出每台电脑的坐标,再给出指令,维修一台电脑或是查询某两台电脑是否能够相互联系。对于每个查询指令输出答案。
思路:很普通的并查集。
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <cmath>
4 using namespace std;
5 const int N = 1010;
6 struct node{
7 double x, y;
8 }c[N];
9 int n, d, f[N], g[N];
10 bool vis[N];
11
12 int find(int x);
13 void merge(int x, int y);
14 bool check(node a, node b);
15 void init();
16
17 int main()
18 {
19 scanf("%d%d", &n, &d);
20 init();
21 for (int i=1;i<=n;i++)
22 {
23 scanf("%lf%lf", &c[i].x, &c[i].y);
24 }
25 char op;
26 int num, cnt = 0, x, y;
27 while (~scanf("%c", &op))
28 {
29 if (op == 'O')
30 {
31 scanf("%d", &num);
32 if (vis[num] == false)
33 {
34 for (int i=1;i<=cnt;i++)
35 {
36 if (check(c[g[i]], c[num]))
37 merge(g[i], num);
38 }
39 cnt++;
40 g[cnt] = num;
41 vis[num] = true;
42 }
43 }
44 else if (op == 'S')
45 {
46 scanf("%d %d", &x, &y);
47 if (find(x) == find(y))
48 puts("SUCCESS");
49 else
50 puts("FAIL");
51 }
52 }
53 return 0;
54 }
55
56 int find(int x)
57 {
58 return f[x] == x ? x : f[x] = find(f[x]);
59 }
60 void merge(int x, int y)
61 {
62 int fx = find(x);
63 int fy = find(y);
64 if (fx != fy)
65 {
66 f[fx] = fy;
67 }
68 }
69 bool check(node a, node b)
70 {
71 double dis = sqrt( (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
72 return dis <= d;
73 }
74 void init()
75 {
76 for (int i=1;i<=n;i++)
77 f[i] = i;
78 }
2、
The Suspects
题意:学校里有n个学生,m个小组。小组中有一个人疑似得了冠状,那么全小组的人都可能得了。给出每个小组的成员,学生0疑似得了冠状,那么求有多少个人被怀疑。
思路:比第一题更普通的并查集。
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5 const int N = 3e4+5;
6 int f[N], n, m;
7
8 int find(int x);
9 void merge(int x, int y);
10 void init();
11
12 int main()
13 {
14 while (scanf("%d %d", &n, &m) && (n || m))
15 {
16 init();
17 for (int i=1;i<=m;i++)
18 {
19 int k, y;
20 scanf("%d", &k);
21 scanf("%d", &y);
22 for (int j=2;j<=k;j++)
23 {
24 int x;
25 scanf("%d", &x);
26 merge(y, x);
27 }
28 }
29 int cnt = 0;
30 for (int i=0;i<n;i++)
31 {
32 if (find(i) == 0)
33 cnt++;
34 }
35 if (m == 0) cnt = 1;
36 printf("%d
", cnt);
37 }
38 return 0;
39 }
40
41 int find(int x)
42 {
43 return f[x] == x ? x : f[x] = find(f[x]);
44 }
45 void merge(int x, int y)
46 {
47 int fx = find(x);
48 int fy = find(y);
49 if (fx != fy)
50 f[max(fx, fy)] = min(fx, fy);
51
52 }
53 void init()
54 {
55 for (int i=0;i<n;i++)
56 f[i] = i;
57 }
3、
How Many Tables
HDU - 1213 1 #include <cstdio>
2 #include <cstring>
3 using namespace std;
4 const int N = 1010;
5 int T, n, m, f[N];
6 bool vis[N];
7
8 int find(int x);
9 void merge(int x, int y);
10 void init();
11
12 int main()
13 {
14 scanf("%d", &T);
15 while (T--)
16 {
17 memset(vis, 0, sizeof(vis));
18 scanf("%d %d", &n, &m);
19 init();
20 for (int i=1;i<=m;i++)
21 {
22 int x, y;
23 scanf("%d %d", &x, &y);
24 merge(x, y);
25 }
26 int cnt = 0;
27 for (int i=1;i<=n;i++)
28 {
29 int k = find(i);
30 if (vis[k] == false)
31 {
32 vis[k] = true;
33 cnt++;
34 }
35 }
36 printf("%d
", cnt);
37 }
38 return 0;
39 }
40
41 int find(int x)
42 {
43 return f[x] == x ? x : f[x] = find(f[x]);
44 }
45 void merge(int x, int y)
46 {
47 int fx = find(x);
48 int fy = find(y);
49 if (fx != fy)
50 f[fx] = fy;
51 }
52 void init()
53 {
54 for (int i=1;i<=n;i++)
55 f[i] = i;
56 }
哟,这不并查集吗?几天不见,这么拉了?
4、
How Many Answers Are Wrong
我错了,并查集哥
1 #include <cstdio>
2 #include <cstring>
3 using namespace std;
4 const int N = 2e5+5;
5 int f[N], rela[N], n, m;
6
7 void init();
8 void merge(int x, int y, int num);
9 int find(int x);
10 bool check(int x, int y, int num);
11
12 int main()
13 {
14 while (~scanf("%d %d", &n, &m))
15 {
16 init();
17 int l, r, num, cnt = 0;
18 for (int i=1;i<=m;i++)
19 {
20 scanf("%d %d %d", &l, &r, &num);
21 if (check(l-1, r, num))
22 {
23 merge(l-1, r, num);
24 }
25 else
26 cnt++;
27
28 }
29 printf("%d
", cnt);
30 }
31 return 0;
32 }
33
34 void init()
35 {
36 for (int i=1;i<=n;i++)
37 f[i] = i, rela[i] = 0;
38 }
39 void merge(int x, int y, int num)
40 {
41 int fx = find(x);
42 int fy = find(y);
43 if (fx != fy)
44 {
45 f[fy] = fx;
46 rela[fy] = rela[x] + num - rela[y];
47 }
48 }
49 int find(int x)
50 {
51 if (f[x] == x) return x;
52 int temp = f[x];
53 f[x] = find(f[x]);
54 rela[x] = rela[x] + rela[temp];
55 return f[x];
56 }
57 bool check(int x, int y, int num)
58 {
59 int fx = find(x);
60 int fy = find(y);
61 if (fx != fy)
62 return true;
63 return num == (rela[y] - rela[x]);
64 }
总结:似乎只要这种题型,能量化给出语句之间的关系就可以用带权并查集做了。带权并查集在维护关系数组rela时
在find()函数中,要维护x和x最终的父结点中间每一个节点的权值,这里用的是每个节点本身的权值来更新。
在merge()函数中,要维护fx 和 fy其中一个变成子节点的节点的权值。需要rela[l], rela[r], num。其中num就是量化的关系。
5、
食物链
题意:和上题一样是找错误语句的个数。关系只有吃和不吃。
思路:也是先量化关系,再利用带权并查集。
代码:
1 #include <cstdio>
2 #include <cstring>
3 using namespace std;
4 const int N = 5e4+5;
5 int n, k, d, x, y, f[N], rela[N];
6
7 inline int read();
8 int find(int x);
9 void merge(int x, int y, int d);
10 bool check(int c, int x, int y);
11 void init();
12
13 int main()
14 {
15 n = read(); k = read();
16 init();
17 int cnt = 0;
18 for (int i=1;i<=k;i++)
19 {
20 d = read(); x = read(); y = read();
21 d--;
22 if (check(d, x, y))
23 merge(x, y, d);
24 else
25 cnt++;
26 }
27 printf("%d
", cnt);
28 return 0;
29 }
30
31 void init()
32 {
33 for (int i=1;i<=n;i++)
34 f[i] = i, rela[i] = 0;
35 }
36 bool check(int d, int x, int y)
37 {
38 if (x > n || y > n || (d == 2 && x == y))
39 return false;
40 if (find(x) == find(y))
41 return ((rela[x] - rela[y] + 3) % 3 ) == d;
42 else
43 return true;
44 }
45 int find(int x)
46 {
47 if (x == f[x]) return x;
48 int t = f[x];
49 f[x] = find(f[x]);
50 rela[x] = (rela[x] + rela[t]) % 3;
51 return f[x];
52 }
53 void merge(int x, int y, int d)
54 {
55 int fx = find(x);
56 int fy = find(y);
57 if (fx != fy)
58 {
59 f[fx] = fy;
60 rela[fx] = (rela[y] - rela[x] + d + 3) % 3;
61 }
62 }
63
64 inline int read()
65 {
66 int x = 0, w = 1;
67 char ch = 0;
68 while (ch < '-')
69 {
70 if (ch == '-')
71 w = -1;
72 ch = getchar();
73 }
74 while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
75 return x * w;
76 }
总结:我是先做的这一题再做的第4题,这一题时对带权并查集认识还不深。所以也是看了别人的博客,这里面不但讲了带权并查集,也列了表描述这一题的关系,看题解还是推荐看这个。
6、
True Liars
题意:一群天使和恶魔住在一个岛上,天使只说真话,恶魔只说假话。向xi号人问第yi号人是否是天使并得到回答。问能否得到哪一部分人是天使,只能有唯一解。
思路:思考一下可知,若回答为yes则两人为同类,回答为no则两人为异类。用带权并查集可以将他们分为一个个集合,每个集合中有两部分人。然后将这些集合组成起来看是否能得到解。用动态规划可以得出解的最大数量。如果不是1,则打印no。如果是1,那我也不知道怎么办了。这题太考验编码能力了,思路倒是比较简单。
半成品代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <vector>
4 using namespace std;
5 const int N = 1010, M = 305;
6 int n, p1, p2, f[M], rela[M], dp[M][M], a[M], b[2][M];
7 bool vis[M];
8 int find(int x);
9 void init();
10
11 int main()
12 {
13 while (~scanf("%d %d %d", &n, &p1, &p2) && n && p1 && p2)
14 {
15 init();
16 while (n--)
17 {
18 int x, y; char s[10];
19 scanf("%d %d %s", &x, &y, s);
20 int flag = (s[0] == 'y');
21 int fx = find(x), fy = find(y);
22 if (fx != fy)
23 {
24 f[fy] = fx;
25 rela[fy] = (rela[x] + rela[y] + flag) % 2;
26 }
27 }
28 int cnt = 0;
29 memset(vis, 0, sizeof(vis));
30 memset(b, 0, sizeof(b));
31 for (int i=1;i<=p1+p2;i++)
32 {
33 if (vis[f[i]] == 0)
34 {
35 vis[f[i]] = true;
36 a[++cnt] = f[i];
37 }
38 b[rela[i]][f[i]]++;
39 }
40 for (int i=1;i<=cnt;i++)
41 {
42 for (int j=p1+p2;j>=1;j--)
43 {
44 if (j >= b[0][a[i]] && dp[i-1][j-b[0][a[i]]])
45 dp[i][j] += dp[i-1][j-b[0][a[i]]];
46 if (j >= b[1][a[i]] && dp[i-1][j-b[1][a[i]]])
47 dp[i][j] += dp[i-1][j-b[1][a[i]]];
48 }
49 }
50 if (dp[cnt][p1] != 1)
51 {
52 puts("no");
53 continue;
54 }
55
56 }
57 return 0;
58 }
59
60 int find(int x)
61 {
62 if (f[x] == x)
63 return x;
64 int temp = f[x];
65 f[x] = find(f[x]);
66 rela[x] = (rela[x] + rela[temp]) % 2;
67 return f[x];
68 }
69 void init()
70 {
71 for (int i=1;i<=p1+p2;i++)
72 f[i] = i, rela[i] = 0;
73 }
总结:我要好好去提升自己的编码能力了,等我足够强了会回来补这题的(大概)。
7、
Supermarket
题意:超市有n样东西要卖,每个东西都有自己的价值和过期时间。一天只能卖一样东西,过期后无法再卖。问能卖到最多多少钱?
思路1:贪心:
对物品以价值从大到小排序。按顺序从价值最大的物品到价值最小的物品,对每一个物品从过期时间开始向前寻找没有卖东西的日子,在可卖出的最晚时间卖出。虽然是O(n^2)也可以AC。
代码1:
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5 const int N = 1e4+5;
6 struct product
7 {
8 int p, d;
9 } a[N];
10 int n;
11 bool vis[N];
12
13 bool cmp(product a, product b)
14 {
15 return a.p > b.p;
16 }
17
18 int main()
19 {
20 while (~scanf("%d", &n))
21 {
22 memset(vis, 0, sizeof(vis));
23 for (int i=1; i<=n; i++)
24 scanf("%d %d", &a[i].p, &a[i].d);
25 sort(a+1, a+1+n,cmp);
26 int ans = 0;
27 for (int i=1; i<=n; i++)
28 {
29 if (vis[a[i].d])
30 {
31 for (int j=a[i].d - 1; j >= 1; j--)
32 {
33 if (!vis[j])
34 {
35 vis[j] = true;
36 ans += a[i].p;
37 break;
38 }
39 }
40 }
41 else
42 {
43 ans += a[i].p;
44 vis[a[i].d] = true;
45 }
46 }
47 printf("%d
", ans);
48 }
49 return 0;
50 }
思路2:用并查集优化思路1的代码。
用f数组记录最近的空闲日期。当前日期被使用后,将其向前移,寻找最近的空闲日期,并记录。说是并查集优化,其实是优化了路径。
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5 const int N = 1e4+5;
6 struct product
7 {
8 int p, d;
9 } a[N];
10 int n;
11 int f[N];
12
13 bool cmp(product a, product b)
14 {
15 return a.p > b.p;
16 }
17 int find(int x)
18 {
19 return f[x] == -1 ? x : f[x] = find(f[x]);
20 }
21
22 int main()
23 {
24 while (~scanf("%d", &n))
25 {
26 memset(f, -1, sizeof(f));
27 for (int i=1;i<=n;i++)
28 scanf("%d %d", &a[i].p, &a[i].d);
29 sort(a+1, a+n+1, cmp);
30 int ans = 0;
31 for (int i=1;i<=n;i++)
32 {
33 int d = find(a[i].d);
34 if (d > 0)
35 {
36 f[d] = d - 1;
37 ans += a[i].p;
38 }
39 }
40 printf("%d
", ans);
41 }
42 return 0;
43 }
思路3:动态规划,人称小贪心。耗时大大增加
状态:dp[j]表示卖出j件物品得到的价值最大值。
状态转移方程:dp[j] = max(dp[j], dp[j-1]+v[i])。
细节:每样东西可卖可不卖,就是01背包。加上时间限制的话,第二层循环就在d[i]...1即可。
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5 const int N = 1e4+5;
6 struct product
7 {
8 int p, d;
9 } a[N];
10 int n, dp[N];
11
12 bool cmp(product a, product b)
13 {
14 return a.d < b.d;
15 }
16
17 int main()
18 {
19 while (~scanf("%d", &n))
20 {
21 memset(dp, 0, sizeof(dp));
22 for (int i=1;i<=n;i++)
23 scanf("%d %d", &a[i].p, &a[i].d);
24 sort(a+1, a+1+n, cmp);
25 int ans = 0;
26 for (int i=1;i<=n;i++)
27 for (int j=a[i].d;j > 0;j--)
28 {
29 dp[j] = max(dp[j], dp[j-1]+a[i].p), ans = max(ans, dp[j]);
30 }
31 printf("%d
", ans);
32 }
33 return 0;
34 }
8、
Parity game POJ - 1733 (离散化+带权并查集)
9、
Navigation Nightmare POJ - 1984(带权并查集)
10、
A Bug's Life
代码:
1 #include <cstdio>
2 #include <cstring>
3 using namespace std;
4 const int N = 2020;
5 int n, m, f[N], rela[N];
6 bool flag;
7
8 void init();
9 int find(int x);
10 void solve(int x, int y);
11
12 int main()
13 {
14 int T;
15 scanf("%d", &T);
16 for (int I=1;I<=T;I++)
17 {
18 flag = false;
19 scanf("%d%d", &n, &m);
20 init();
21 for (int i=1;i<=m;i++)
22 {
23 int x, y;
24 scanf("%d %d", &x, &y);
25 if (flag) continue;
26 solve(x, y);
27 }
28 printf("Scenario #%d:
", I);
29 if (flag)
30 printf("Suspicious bugs found!
");
31 else
32 printf("No suspicious bugs found!
");
33 puts("");
34 }
35 return 0;
36 }
37
38 void init()
39 {
40 for (int i=1;i<=n;i++)
41 f[i] = i, rela[i] = 0;
42 }
43 int find(int x)
44 {
45 if (f[x] == x) return x;
46 int temp = f[x];
47 f[x] = find(f[x]);
48 rela[x] = (rela[x] + rela[temp]) % 2;
49 return f[x];
50 }
51 void solve(int x, int y)
52 {
53 int fx = find(x), fy = find(y);
54 if (fx != fy)
55 {
56 f[fy] = fx;
57 rela[fy] = (rela[x] + rela[y] + 1) % 2;
58 }else
59 {
60 if (rela[x] == rela[y])
61 flag = true;
62 }
63 }
这题离谱,这个代码700+ms,用了快读1500+ms,poj上却有0ms的。不懂=。=
11、