用spfa 算法求关于有负权边的最短路。
每次从队列中取出一个节点X,遍历与X相通的Y节点,查询比对 Y的长度 和 X的长度+ X与Y的长度
如果X的长度+ X与Y的长度 > Y的长度,说明需要更新操作。
1).存入最短路。
2).由于改变了原有的长度,所以需要往后更新,与这个节点相连的最短路。(即:判断下是否在队列,在就不用重复,不在就加入队列,等待更新)。
3).在这期间可以记录这个节点的进队次数,判断是否存在负环。
4.直到队空。
判断有无负环:如果某个点进入队列的次数超过N次则存在负环。
给出spfa 模;板
bool spfa(int s){
queue q;
for(int i=0;i<=n;i++)
ksum[i] = vis[i] = 0,dis[i] = INF;
dis[s] = 0,vis[s] = 1,q.push(s);
while(q.size()){
int u = q.front();
q.pop();
vis[u] = 0; //这里需要注意,因为存在负权,spfa每个节点需要重复入队
for(int i = head[u];~i;i = nex[i]){
int v = to[i],w=edge[i];
if(dis[u] + w < dis[v]){
dis[v] = dis[u] + w;
if(!vis[v]){
q.push(v); vis[v] = 1;
if(++ksum[v]> n){
return true;
}
}
}
}
}
return false;
}
给出模板题:http://poj.org/problem?id=3259
//#include
#include
#include
#include
using namespace std;
const int N =3e3;
const int INF = 1e9;
int to[N<<1],nex[N<<1],edge[N<<1];
int n,m,k,cnt;
int dis[N],vis[N],ksum[N],head[N];
void add(int x,int y,int w){
to[++cnt]= y;
nex[cnt]= head[x];
edge[cnt] = w;
head[x] = cnt;
}
bool spfa(int s){
queue q;
for(int i=0;i<=n;i++)
ksum[i] = vis[i] = 0,dis[i] = INF;
dis[s] = 0,vis[s] = 1,q.push(s);
while(q.size()){
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = head[u];~i;i = nex[i]){
int v = to[i],w=edge[i];
if(dis[u] + w < dis[v]){
dis[v] = dis[u] + w;
if(!vis[v]){
q.push(v); vis[v] = 1;
if(++ksum[v]> n){
return true;
}
}
}
}
}
return false;
}
int main(){
int t;scanf("%d",&t);
while(t--){
cnt = 0;
memset(head,-1,sizeof head);
scanf("%d%d%d",&n,&m,&k);
for(int i = 1,x,y,w;i <= m; i ++){
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);
add(y,x,w);
}
for(int i = 1,u,v,w;i <= k; i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,-w);
}
if(spfa(1)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
再给一题签到题:https://nanti.jisuanke.com/t/41305
#include
using namespace std;
typedef long long ll;
const int N = 2e5+10;
const int INF = 2e9;
int to[N<<1],nex[N<<1];ll edeg[N<<1];
int n,m,k,cnt;
int head[N];
bool vis[N];
ll dis[N];
void add(int x,int y,int w){
to[++cnt]= y;
nex[cnt]= head[x];
edeg[cnt] = w;
head[x] = cnt;
}
void spfa(int s){
queue q;
for(int i=0;i<=n;i++)
vis[i] = false,dis[i] = INF;
vis[s]=true,dis[s] = 0,q.push(s);
while(q.size()){
int u = q.front();
q.pop();
vis[u] = false; //这里需要注意,因为存在负权,spfa每个节点需要重复入队
for(int i=head[u];~i;i = nex[i]){
int v = to[i];
if(dis[v] > dis[u]+ edeg[i]){
dis[v] = dis[u] + edeg[i];
if(!vis[v]){
q.push(v);
vis[v] = true;
}
}
}
}
}
int main(){
int t;scanf("%d",&t);
while(t--){
cnt = 0;
memset(head,-1,sizeof head);
scanf("%d%d",&n,&m);
for(int i = 1,u,v,w;i <= m;i ++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
for(int i=1;i<=6;i++){
int s,t;scanf("%d%d",&s,&t);
spfa(t);
printf("%lld\n",-dis[s]);
add(s,t,-dis[s]);
}
}
return 0;
}