2021-10-7

本文深入探讨了图论中的SPFA最短路径算法和并查集在解决实际问题中的应用。针对有负环的最短路径问题,文章通过SPFA演示了如何进行二分搜索来求解。同时,文章分析了并查集在处理路径压缩和边权求和情况下的实现细节,并给出了实例说明。这两个数据结构在解决复杂问题时展现出的强大功能,对于提升算法解决实际问题的能力具有重要意义。
摘要由CSDN通过智能技术生成

Sightseeing Cows

首先,我们要明白,这道题可能有负环,因此,我们要用SPFA来做,别问,问就是不会,然后可以二分,因为是个0/1分数规划,所以对于任意的环,求出一个mid并且判断二者关系后就可以二分了。
**注意!!!**每次我们要建一个新图跑SPFA求最短路。(在pd()里面)

#include<bits/stdc++.h>
#define N 1006
#define M 5006
#define eps 1e-6
using namespace std;
int n,m,c[N],f[N],x[M],y[M],z[M];
int head[N],to[M],nex[M],tot;//建图 
double val[M],d[N];
bool v[N];
void add(int x,int y,double z){
	to[++tot]=y;
	nex[tot]=head[x];
	head[x]=tot;
	val[tot]=z;
}
bool spfa(){
	queue<int>q;//队列 
	for (int i = 1; i <= n; i++) {
		q.push(i);
		d[i]=0;//长度 
		v[i]=1;//是否在队列中 
	}
	memset(c, 0, sizeof(c));
	while (q.size()){
		int x = q.front();
		q.pop();
		v[x] = 0;
		for (int i=head[x];i;i=nex[i]){
			int y=to[i];
			if (d[y] > d[x] + val[i]) {
				d[y] = d[x] + val[i];
				if(++c[y]>n)return 1;
				if(!v[y]){
					q.push(y);
					v[y]=1;
				}
			}
		}
	}
	return 0;
}
bool pd(double w) {
	tot=0;
	memset(head, 0, sizeof(head));
	for(int i=1;i<=m;i++)add(x[i],y[i],w*z[i]-f[x[i]]);
	return spfa();
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)scanf("%d", &f[i]);
	for (int i=1;i<=m;i++)scanf("%d%d%d",&x[i],&y[i],&z[i]);
	double l=0,r=1000;
	while(r-l>eps){
		double mid=(l+r)/2;
		if(pd(mid))l=mid;
		else r=mid;
	}
	printf("%.2f",l);
	return 0;
}

银河英雄传说

莱因哈特为什么不直接干呢,一看就知道是并查集,但是跟普通的不太一样,因此在mergeget函数里面要加点东西记录位置和长度,就用size存大小,d存位置,就好了
还有一点,root是不能省的,因为要存初始状态,不然就存了个寂寞,都在code里了

#include<bits/stdc++.h>
#define N 30001
using namespace std;
int n,fa[N],d[N],size[N],l,r;
char opt[2];
int get(int x){
	if(fa[x]==x)return x;
	int root=get(fa[x]);//这里要存初始状态,不然就要W掉90% 
	d[x]+=d[fa[x]];//求边权
	return fa[x]=root;
}
void merge(int x,int y){//合并x到y 
	x=get(x),y=get(y);
	fa[x]=y,d[x]=size[y];
	size[y]+=size[x];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=N;i++)fa[i]=i;
	for(int i=1;i<=N;i++)size[i]=1;
	while(n--){
		scanf("%s%d%d",opt,&l,&r);
		if(opt[0]=='M')merge(l,r);
		else {
			if(get(l)!=get(r))printf("-1\n");
			else printf("%d\n",abs(d[l]-d[r])-1);
		}
	}
} 

搭配购买(buy)

!背包!,嗯……还有并查集!,简直不要太简单,把要存到一起的合并了就OK,不过要判断他们是不是本来就在一起,然后就好了。

#include<bits/stdc++.h>
#define N 10001
#define INF 100000
using namespace std;
int n,m,fa[N],w1[N],w2[N],l,r,tot=0,w,val1[N],val2[N];
int f[N];
int get(int x){
	return fa[x]==x?x:fa[x]=get(fa[x]);//路径压缩
}
void merge(int x,int y){//合并x到y
	fa[y]=x,w1[x]+=w1[y],val1[x]+=val1[y];
//	w1[y]=INF,val1[y]=0;
}
int main(){
	scanf("%d%d%d",&n,&m,&w);
	for(int i=1;i<=n;i++)scanf("%d%d",&w1[i],&val1[i]);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&l,&r);
		l=get(l),r=get(r);
		if(l==r)continue;
		merge(l,r);
	}
	memset(f,0xcf,sizeof(f));
	f[0]=0;
	for(int i=1;i<=n;i++){
		if(fa[i]==i)
		for(int j=w;j>=w1[i];j--){
			f[j]=max(f[j],f[j-w1[i]]+val1[i]);
		}
	}
	int ans=0;
	for(int i=0;i<=w;i++)ans=max(ans,f[i]);
	printf("%d",ans);
}

溜溜球。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值