【洛谷】P1455 搭配购买**

穿越隧道

并查集和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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值