搭配购买
并查集+01背包;
先合并分块,然后按照01背包模型求解
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
int p[N], c[N], d[N];
int v[N], w[N];
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
int f[N];
void solve() {
int n, m, W;
cin >> n >> m >> W;
for (int i = 1; i <= n; i++) cin >> c[i] >> d[i], p[i] = i;
while (m--) {
int u, v;
cin >> u >> v;
if (find(u) == find(v)) continue;
else {
c[find(u)] += c[find(v)];
d[find(u)] += d[find(v)];
p[find(v)] = find(u);
}
}
int cnt = 1;
for (int i = 1; i <= n; i++) {
if (find(i) == i) v[cnt] = c[find(i)], w[cnt] = d[find(i)], cnt++;
}
for (int i = 1; i < cnt; i++) {
for (int j = W; j >= v[i]; j--) {
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
cout << f[W] << endl;
}
int main() {
solve();
return 0;
}
//维护集合大小使用size数组存储大小即可;
维护每个点到根节点的距离:
int find(int x){
if(x!=p[x]){
int root=find(p[x]);
d[x]+=d[p[x]];
p[x]=root;
}
return p[x];
}
Wireless Network
裸并查集+枚举
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1010;
struct node {
int x, y;
} pos[N];
int vis[N];//只有已经被维修的电脑才能连在一起;
int p[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void solve() {
int n, d;
scanf("%d%d", &n, &d);
for (int i = 1; i <= n; i++) p[i] = i, scanf("%d%d", &pos[i].x, &pos[i].y);
char op[2];
int m;
int a, b;
while (~scanf("%s", op)) {
if (op[0] == 'O') {
scanf("%d", &m);
vis[m] = 1;//被维修;
for (int i = 1; i <= n; i++) {
if ((pos[i].x - pos[m].x) * (pos[i].x - pos[m].x) + (pos[i].y - pos[m].y) * (pos[i].y - pos[m].y) <= d * d and vis[i]) {//判断距离
int pi = find(i), pm = find(m);
p[pi] = pm;
}
}
} else {
scanf("%d%d", &a, &b);
if (find(a) == find(b)) puts("SUCCESS");
else puts("FAIL");
}
}
}
int main() {
solve();
return 0;
}
The Suspects
维护size的并查集
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 30010;
int p[N], s[N];
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
void solve() {
int n, m;
while (scanf("%d%d", &n, &m), n) {
for (int i = 0; i < n; i++) p[i] = i, s[i] = 1;
while (m--) {
int k, x, lst;
scanf("%d", &k);
int cnt = k;
while (cnt--) {
scanf("%d", &x);
if (cnt == k - 1) {
lst = x;
continue;
}
int px = find(x), pl = find(lst);
if (px != pl) {
s[pl] += s[px];
p[px] = pl;
}
lst = x;
}
}
printf("%d\n", s[find(0)]);
}
}
int main() {
solve();
return 0;
}
How Many Tables
裸并查集统计个数;
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int p[N];
int find(int x){
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void solve() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) p[i] = i;
while (m--) {
int a, b;
cin >> a >> b;
int pa = find(a), pb = find(b);
p[pa] = pb;
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (find(i) == i) cnt ++;
}
cout << cnt << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
How Many Answers Are Wrong
带权并查集,维护到根节点的距离;
假如两个集合不在一起时,需要合并;
如果两个集合属于同一个根,则需要判断;
合并集合的时候只改变根节点的距离;
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200010;
int p[N], d[N];
int find(int x) {
if (x != p[x]) {
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}
void solve() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
int a, b, s;
int cnt = 0;
for (int i = 1; i <= n; i++) p[i] = i, d[i] = 0;
while (m--) {
scanf("%d%d%d", &a, &b, &s);
a--;
int pa = find(a), pb = find(b);
if (pa == pb) {
if (d[b] - d[a] != s) cnt++;
} else {
p[pb] = pa;
d[pb] = d[a] + s - d[b];//假如pb=C,把三角形abc拉成变成一条直线,就能得出这个结论;
}
}
printf("%d\n", cnt);
}
}
int main() {
solve();
return 0;
}
食物链
带权并查集
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 50010;
int p[N], d[N];
int find(int x) {
if (x != p[x]) {
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}
void solve() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) p[i] = i;
int res = 0;
while (k--) {
int s, a, b;
scanf("%d%d%d", &s, &a, &b);
if (a > n or b > n) res++;
else {
int pa = find(a), pb = find(b);
if (s == 1) {
if (pa == pb and (d[a] - d[b]) % 3) res++;
else if (pa != pb) {
p[pa] = pb;
d[pa] = d[b] - d[a];
}
} else {
if (a == b) res++;
else if (pa == pb and (d[a] - d[b] - 1) % 3) res++;
else if (pa != pb) {
p[pa] = pb;
d[pa] = d[b] + 1 - d[a];
}
}
}
}
printf("%d\n", res);
}
int main() {
solve();
return 0;
}
并查集刷题心得:带权并查集,当集合合并时,改变的是未合并时根节点到祖宗节点的距离;这个可以通过三者之间的关系确定;
小希的迷宫
思路,假如两个点已经在同一个集合,那么肯定会有环出现;
最后判断可以判断集合是否只有一个,或者点数减边数等于1;
//点数减边数为1;
//维护size集合;
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int p[100005], success;
int s[100005];
int edge;
int lst;
void init() {
for (int i = 0; i < 100005; i++) {
p[i] = i;
s[i]=1;
}
}
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]);
}
void merge(int x, int y) {
int px = find(x);
int py = find(y);
if (px != py) {
s[py]+=s[px];
p[px] = py;
} else
success = 0;
}
bool check(){
return s[find(lst)]-edge==1;
}
int main() {
int n, m, a, b;
while (scanf("%d %d", &n, &m) != EOF) {
if (m == 0 and n == 0) {
printf("Yes\n");
continue;
}
init();
if (m == -1 and n == -1) {
break;
}
merge(m, n);
edge=1;
success = 1;
// vis[n] = vis[m] = 1;
while (scanf("%d %d", &a, &b) , a and b) {
merge(a, b);
lst=a;
// vis[a] = 1, vis[b] = 1;
range++;
}
if (success == 0) {
printf("No\n");
} else {
if (check()) {
printf("Yes\n");
} else {
printf("No\n");
}
}
}
return 0;
}
//判断集合是否只有一个
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int p[100005], success;
int vis[100005];
void init() {
for (int i = 0; i < 100005; i++) {
p[i] = i;
vis[i] = 0;
}
}
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]);
}
void merge(int x, int y) {
int px = find(x);
int py = find(y);
if (px != py) {
p[px] = py;
} else
success = 0;
}
bool check(){
int sum=0;
for(int i=1;i<=100005;i++){
if(vis[i] and p[i]==i) sum++;
}
return sum==1;
}
int main() {
int n, m, a, b;
while (scanf("%d %d", &n, &m) != EOF) {
if (m == 0 and n == 0) {
printf("Yes\n");
continue;
}
init();
if (m == -1 and n == -1) {
break;
}
merge(m, n);
success = 1;
vis[n] = vis[m] = 1;
while (scanf("%d %d", &a, &b) , a and b) {
merge(a, b);
vis[a] = 1, vis[b] = 1;
}
if (success == 0) {
printf("No\n");
} else {
if (check()) {
printf("Yes\n");
} else {
printf("No\n");
}
}
}
return 0;
}
//这里并查集的作用是用于记录时间点;
//贪心思路:按照利润大小排序;然后选择最大的利润再让p[px]=px-1;
//说明假如还有同一个时间点的就排到前一个位置;
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 10010;
int p[N];
struct node {
int x, d;
} a[N];
bool cmp(node a, node b) {
return a.x > b.x;
}
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
void inti() {
for (int i = 1; i <= N; i++) p[i] = i;
}
void solve() {
int n;
while (~scanf("%d", &n)) {
inti();
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].x, &a[i].d);
}
sort(a + 1, a + 1 + n, cmp);
int ans = 0;
for (int i = 1; i <= n; i++) {
int pa = find(a[i].d);
if (pa > 0) {
ans += a[i].x;
p[pa] = pa - 1;
}
}
printf("%d\n", ans);
}
}
int main() {
solve();
return 0;
}
A Bug’s Life
带权分类并查集
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int p[2010], d[2010];
int Case = 1;
int find(int x) {
if (x != p[x]) {
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}
void join(int a, int b) {
int pa = find(a), pb = find(b);
p[pa] = pb;
d[pa] = d[b] - d[a] + 1;
}
void solve() {
int n, m;
int check = 1;
scanf("%d%d", &n, &m);
int a, b;
for (int i = 1; i <= n; i++) p[i] = i,d[i]=0;
while (m--) {
scanf("%d%d", &a, &b);
int pa = find(a), pb = find(b);
if (pa != pb) join(a, b);
else if (pa == pb) {
if (abs(d[a] % 2 - d[b] % 2) == 0) check = 0;
}
}
printf("Scenario #%d:\n", Case++);
if (check) printf("No suspicious bugs found!\n");
else printf("Suspicious bugs found!\n");
putchar('\n');
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
is a tree?
判断是不是树的条件;
1.除了根节点剩下的入度都是1;(有向)
2.只有一个根节点;
3.空树也算,环不能算,包括自环;
//以后保证再不改代码了,越改越乱qwq
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int p[100005], success;
int vis[100005];
int d[100005];
int edge;
int lst;
int Case=1;
void init() {
for (int i = 0; i < 100005; i++) {
p[i] = i;
vis[i]=0;
d[i]=0;
}
}
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]);
}
void merge(int x, int y) {
int px = find(x);
int py = find(y);
if(x==y) success=0;
else if (px != py and !d[y]) {
// s[px]+=s[py];
p[py] = px;
d[y]=1;
} else
success = 0;
}
bool check(){
int sum=0;
for(int i=1;i<=100005;i++){
if(find(i)==i and vis[i]) sum++;
}
return sum==1;
}
int main() {
int n, m, a, b;
while (scanf("%d %d", &n, &m) != EOF) {
if (m == 0 and n == 0) {
printf("Case %d is a tree.\n",Case++);
continue;
}
init();
if (m == -1 and n == -1) {
break;
}
success = 1;
merge(n, m);
edge=1;
vis[n] = vis[m] = 1;
while (scanf("%d %d", &a, &b) , a and b) {
merge(a, b);
lst=a;
vis[a] = 1, vis[b] = 1;
edge++;
}
printf("Case %d ",Case++);
if (success == 0) {
printf("is not a tree.\n");
} else {
if (check()) {
printf("is a tree.\n");
} else {
printf("is not a tree.\n");
}
}
}
return 0;
}