并查集和01背包的搭配
01背包需优化,防止TLE.
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int p[N];
int n,m,k;
int w[N];//第i朵云的价钱
int v[N];//第i朵云的价值
int f[N];
int ans;
int find(int x){
if(p[x] != x){
p[x] = find(p[x]);//路径压缩,直接除根节点外的其余结点,直接和根结点相连。
}
return p[x];
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i = 1; i <= n; i++) p[i] = i;
int a,b;
for(int i = 1; i <= n; i++){
scanf("%d%d",&w[i],&v[i]);
// p[find(a)] = find(b);
}
for(int i = 1; i <= m; i++){
scanf("%d%d",&a,&b);
p[find(a)] = find(b);
//下面注释的代码只在1到m个结点,统计了根节点的总价值和总花费
// w[b] += w[a];//这个集合根节点的需要花的钱。
// w[a] = 0;
// v[b] += v[a];//这个集合根节点的最大价值
// v[a] = 0;
}
for(int i = 1; i <= n; i++){
if(p[i] != i){//如果不是根节点,防止根结点自身的价值和花费翻倍
w[find(i)] += w[i];
w[i] = 0;
v[find(i)] += v[i];
v[i] = 0;
}
}
//若为下面注释的代码,加上不把w[i]和v[i],更新为0,则只能得90分
// for(int i = 1; i <= n; i++){
// for(int j = k; j >= w[find(i)]; j--){
// f[j] = max(f[j],f[j - w[find(i)]] + v[find(i)]);
// }
// }
//有些不理解,为啥上面和下面两端代码不等价。貌似懂了,当把几个云朵得价值聚集在根节点时,相当于选了这个集合中得所有云朵
//
for(int i = 1; i <= n; i++){
for(int j = k; j >= w[i]; j--){
f[j] = max(f[j],f[j - w[i]] + v[i]);
}
}
printf("%d\n",f[k]);
return 0;
}
9/11 (AcWing)
只能过9个样例
估计是并查集合并的时候,出了问题。
破案了,要将根节点的sum加到另一个根节点上
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n,m,W;
int v[N],w[N];
int f[N];
int a[N],b[N];
int p[N],sum[N],sumw[N];
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);
}
return p[x];
}
int main(){
scanf("%d%d%d",&n,&m,&W);
for(int i = 1; i <= n; i++) p[i] = i;
for(int i = 1; i <= n; i++){
scanf("%d%d",&v[i],&w[i]);
sum[i] = v[i];
sumw[i] = w[i];
}
for(int i = 0; i < m; i++){
int a,b;
scanf("%d%d",&a,&b);
int pa = find(a), pb = find(b);
if(pa != pb){
sum[pb] += sum[a];//将一个集合中的所有的钱集中到祖宗结点
sumw[pb] += sumw[a];
sumw[a] = 0;
sum[a] = 0;//
p[a] = pb;
}
else{
if(b != pb){
sum[pb] += sum[b];
sum[b] = 0;
sumw[pb] += sumw[b];
}
if(a != pa){
sum[pa] += sum[a];
sum[a] = 0;
sumw[pa] += sumw[a];
}
}
}
// int k = 0;
// for(int i = 1; i <= n; i++){
// if(sum[i]){
cout << "i = " << i <<"sum[i] = " << sum[i] << endl;
// a[++k] = sum[i];
// b[k] = sumw[i];
// }
// }
// for(int i = 1; i <= k; i++){
// for(int j = W; j >= a[i]; j--){
// f[j] = max(f[j],f[j - a[i]] + b[i]);
// }
// }
for(int i = 1; i <= n; i++){
for(int j = W; j >= sum[i]; j--){
f[j] = max(f[j],f[j - sum[i]] + sumw[i]);
}
}
cout << f[W] << endl;
return 0;
}
AC代码(野生版)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n,m,W;
int v[N],w[N];
int f[N];
int a[N],b[N];
int p[N],sum[N],sumw[N];
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);
}
return p[x];
}
int main(){
scanf("%d%d%d",&n,&m,&W);
for(int i = 1; i <= n; i++) p[i] = i;
for(int i = 1; i <= n; i++){
scanf("%d%d",&v[i],&w[i]);
sum[i] = v[i];
sumw[i] = w[i];
}
for(int i = 0; i < m; i++){
int a,b;
scanf("%d%d",&a,&b);
int pa = find(a), pb = find(b);
if(pa != pb){
sum[pb] += sum[pa];//将一个集合中的所有的钱集中到祖宗结点
sumw[pb] += sumw[pa];
sumw[pa] = 0;
sum[pa] = 0;//
p[pa] = pb;
}
else{
if(b != pb){
sum[pb] += sum[b];
sum[b] = 0;
sumw[pb] += sumw[b];
}
if(a != pa){
sum[pa] += sum[a];
sum[a] = 0;
sumw[pa] += sumw[a];
}
}
}
for(int i = 1; i <= n; i++){
for(int j = W; j >= sum[i]; j--){
f[j] = max(f[j],f[j - sum[i]] + sumw[i]);
}
}
cout << f[W] << endl;
return 0;
}
y总版
y总的代码好简洁啊,真优美。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n,m,W;
int v[N],w[N];
int f[N];
int p[N];
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);
}
return p[x];
}
int main(){
scanf("%d%d%d",&n,&m,&W);
for(int i = 1; i <= n; i++) p[i] = i;
for(int i = 1; i <= n; i++){
scanf("%d%d",&v[i],&w[i]);
}
for(int i = 0; i < m; i++){
int a,b;
scanf("%d%d",&a,&b);
int pa = find(a), pb = find(b);
if(pa != pb){
v[pb] += v[pa];
w[pb] += w[pa];
p[pa] = pb;
}
}
for(int i = 1; i <= n; i++){
if(p[i] == i){//当为一个集合的根节点时
for(int j = W; j >= v[i]; j--){
f[j] = max(f[j],f[j - v[i]] + w[i]);
}
}
}
cout << f[W] << endl;
return 0;
}