最短路..(有全源以及有负权边的)

有人破防了,我不说是谁,但真的裂开,两个板子都爆炸了真的是nmd个b我受不了,对于这些板子的失误真的让人难以接受,垃圾,还有线段树,服了还得补这一篇博客来补救,我是垃圾,被别人爆杀了。
dj的板子:

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,last[2000000],dis[2000000];
struct pp
{
	int x,y,c,next;
};pp p[2000000];
struct node
{
	int dis,now;
	friend bool operator <(const node &x,const node &y)
	{
		return x.dis>y.dis;
	}; 
};priority_queue<node> q;
bool v[2000000];
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now;
	return ;
}
void dj(int ST)
{
	memset(v,true,sizeof(v));memset(dis,63,sizeof(dis));
	node e;e.now=ST;e.dis=0;q.push(e);dis[ST]=0;
	while(q.size()!=0)
	{
		node x=q.top();q.pop();
		if(v[x.now]==false) continue ;v[x.now]=false;
		for(int i=last[x.now];i!=-1;i=p[i].next)
		{
			int y=p[i].y;
			if(dis[y]>dis[x.now]+p[i].c)
			{
				dis[y]=dis[x.now]+p[i].c;
				e.now=y;e.dis=dis[y];q.push(e);	
			}
		}
	} 
	return ;
}
signed main()
{
	memset(last,-1,sizeof(last));
	int ST;scanf("%lld%lld%lld",&n,&m,&ST);
	for(int i=1;i<=m;i++)
	{
		int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
		ins(x,y,c);	
	}
	dj(ST);
	for(int i=1;i<=n;i++) 
	{
		if(dis[i]!=dis[0]) printf("%lld ",dis[i]);
//		else printf("2147483647 ");
	}
	return 0;
}

注意啦,dj不能处理负权边:https://blog.csdn.net/baidu_31818237/article/details/50611592可以看看,我自己的理解是这个若是出现负权,那么就会违背贪心的本质。也就是走较长路更优。

写一个spfa的真的服了。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int len=0,last[1000001];
int n,m,dis[100001];
int q[100001];
struct pp
{
	int x,y,c,next;
};pp p[1000001];
bool v[1000001];
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now; 
	return ;
}
void spfa(int ST)
{
	memset(dis,63,sizeof(dis));memset(v,true,sizeof(v));
	int st=1,ed=2;q[st]=ST;dis[ST]=0,v[ST]=false;
	while(st!=ed)
	{
		int x=q[st];st++;v[x]=true;
		for(int i=last[x];i!=-1;i=p[i].next)
		{
			int y=p[i].y;
			if(dis[y]>dis[x]+p[i].c)
			{
				dis[y]=dis[x]+p[i].c;
				if(v[y]==true) v[y]=false,q[ed++]=y;
			}
		}
	}
	return ;
} 
signed main()
{
	memset(last,-1,sizeof(last));
	int ST;scanf("%lld%lld%lld",&n,&m,&ST);
	for(int i=1;i<=m;i++)
	{
		int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
		ins(x,y,c);
	}
	spfa(ST);
	for(int i=1;i<=n;i++)
	{
		if(dis[i]!=dis[0]) printf("%d ",dis[i]);
		else printf("2147483647 ");
	}
	return 0;
} 

学一学除spfa外处理负环的方法?也就是dj的处理,并且还能用来处理全源~ 好厉害!
考虑dj的处理,明显处理不了负环,那么考虑将所有的边权变为非负数。一种容易想到的方案是所有的边加上一个值,那么可以给出证明:若是每一条边都加上的一个数,那么一条路径其对结果贡献会加上其路径上边的数量 * 加的那个数,那这样明显是不行的。
那么我们先说做法:先建一个0号点,然后我们从0点跑一遍最短路,记为w[i],网上那些大佬没有说有负环怎么办啊害得我找了半天发现有负环直接判出去就好了呜呜呜。然后将边权转为p[i].c=p[i].c+w[x]-w[y]。那么然后直接跑n遍最短路就行。
给出证明(没有负环哈):哦对其实我们常常能听到一个词——势能,一种思想或者一个定义,取决于个人。问题在于,势能的变化量只和起点和终点的相对位置有关,而与路径无关。也其实算是一种另类的前缀和?好吧不是,比如说位移吧(高中物理一点点),其实就是一种势能的体现?那么其实这样的转化会导致距离变化为p[i].c+p[i…].c+w[1]-w[ed]。可以证明出无论如何后面的那两个值是不变的。且其和一定大于等于0。给出别人的正确性证明?
在这里插入图片描述

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,last[520001],w[520001],dis[520001];
struct node 
{
	int x,dis;friend bool operator < (const node &x,const node &y)
	{
		return x.dis > y.dis;
	};
};priority_queue<node > q2,qq;
struct pp
{
	int x,y,c,next;
};pp p[5200001];
int q1[5200001],num[520001];bool v[520001];
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now;
	return ;	
}
bool spfa(int ST)
{
	memset(v,true,sizeof(v));memset(num,0,sizeof(num));memset(w,63,sizeof(w));w[ST]=0;
	int st=1,ed=2;q1[st]=ST;v[ST]=false;
	while(st!=ed)
	{
		int x=q1[st++];v[x]=true;
		for(int i=last[x];i!=-1;i=p[i].next)
		{
			int y=p[i].y;if(w[y]<=w[x]+p[i].c) continue ;
			w[y]=w[x]+p[i].c;if(!v[y]) continue ;
			num[y]++;if(num[y]>=n+1) return true;
			v[y]=false;q1[ed++]=y; 
		}
	}	
	return false;	
}
void dj(int ST)
{
	memset(v,true,sizeof(v));memset(dis,63,sizeof(dis));q2=qq;dis[ST]=0;
	node e;e.x=ST,e.dis=dis[ST];q2.push(e);
	while(q2.size())
	{
		node x=q2.top();q2.pop();if(!v[x.x]) continue ;v[x.x]=false ;
		for(int i=last[x.x];i!=-1;i=p[i].next)
		{
			int y=p[i].y;if(dis[y]<=dis[x.x]+p[i].c) continue ;
			dis[y]=dis[x.x]+p[i].c;e.dis=dis[y],e.x=y;q2.push(e);
		}
	}
	return ;
}
signed main()
{
	memset(last,-1,sizeof(last));
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
		ins(x,y,c);
	}
	int ST=0;for(int i=1;i<=n;i++) ins(ST,i,0);
	if(spfa(ST)) 
	{
		printf("-1");	
		return 0;
	}
	for(int i=1;i<=m;i++) p[i].c=p[i].c+w[p[i].x]-w[p[i].y];//printf("%lld\n",p[i].c);
	for(int i=1;i<=n;i++)
	{
		dj(i);int ans=0;
		for(int j=1;j<=n;j++)
		{
			if(dis[j]==dis[0]) ans+=(j*1e9);
			else ans+=(j*(dis[j]-w[i]+w[j]));
		}printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值