题解 P3385 【【模板】负环】

(图爆了,重新交一次)

本题我用了dfs与bfs两种解法,但#9显然是在卡dfs,所以遗憾91分。代码附于文末,可供参考。

当bfs中一个点的d值被更新达到n次,说明图中存在负环。

如下图

如果是bfs求最短路的话,势必会在这个环里无限转圈。所以我们用一个下标对应点编号的数组来存这个点被访问的次数
因为一个点最多与n-1个点相连,也就是最多被访问n-1次。所以当这个点被访问到n次时,这条路径上一定有负环。
代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=10005;//好看
int t,n,m,u,v,w,biss[N],b[N],p[N][N],d[N],flag=0;//flag用于判是否已经判了有负环。如果flag被更新,就输出“NO”
struct lyf {
	int v,w;
	lyf(int a,int b) {
		v=a;
		w=b;
	}
};
vector<lyf> g[N];
bool bis[N];
queue<int> q;//队列做bfs
int main() {
	cin>>t;
	for(int o=1;o<=t;o++) {
		cin>>n>>m;
		memset(d,0x7f,sizeof(d));
		memset(b,0,sizeof(b));
		flag=0;//初始化
		for(int i=1; i<=m; i++) {
			cin>>u>>v>>w;
			g[u].push_back(lyf(v,w));
			if(w>=0)g[v].push_back(lyf(u,w));//一定注意!!①题意是非负边双向②注意是非负边!!#9里有个w就是0!
		}
		d[1]=0;
		q.push(1);//题意,从1开始
		while(!q.empty()) {
			u=q.front();
			q.pop();
			bis[u]=0;
			if(b[u]>=n) {
				cout<<"YES"<<endl;
				flag=1;
				break;
			}
			for(int i=0; i<g[u].size(); i++)if(d[u]+g[u][i].w<d[g[u][i].v]) {
					b[g[u][i].v]++;
					d[g[u][i].v]=d[u]+g[u][i].w;
					if(!bis[g[u][i].v]) {
						bis[g[u][i].v]=1;
						q.push(g[u][i].v);
					}
				}
		}
		for(int i=1; i<=n; i++)
			g[i].clear();//清空。不然。。。。。
		if(!flag)
			cout<<"NO"<<endl;
	}
	return 0;
}

dfs则是通过判断有没有重复访问一个相同的点,通过两个bool数组实现。

代码附于下

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int d[N],a,e,c,t,n,m;
struct ljh {
	int v,w;
	ljh(int v,int w):v(v),w(w) {}
};
vector <ljh> g[N];
bool flag,vis[N],b[N];
void dfs(int k) {
	if (flag)
		return;
	vis[k]=true;
	for (int i=0; i<g[k].size(); ++i)
		if (d[k]+g[k][i].w<d[g[k][i].v]) {
			if (vis[g[k][i].v]) {
				flag=true;
				return;
			}
			d[g[k][i].v]=d[k]+g[k][i].w;
			dfs(g[k][i].v);
		}
	vis[k]=false;
}
void lyf(int u) {//遍历1能到的点,也是dfs
	for(int i=0; i<g[u].size(); i++) {
		if(g[u][i].w&&!b[g[u][i].v]) {
			b[g[u][i].v]=1;
			lyf(g[u][i].v);
		}
	}
}
int main() {
	cin>>t;
	for(int i=1; i<=t; i++) {
		cin>>n>>m;
		for(int j=1; j<=m; j++) {
			cin>>a>>e>>c;
			g[a].push_back(ljh(e,c));
			if(c>=0)
			{
				g[e].push_back(ljh(a,c));
			}
		}
		memset(d,0x7f,sizeof(d));
		memset(b,0,sizeof(b));//不对正边扩展,剪枝
		memset(vis,0,sizeof(vis));
		flag=false;
		lyf(1);
		b[1]=1; 
		for (int i=1; i<=n && !flag; ++i)
			if(b[i]==1)
				dfs(i);
		if(flag==0) {
			cout<<"NO"<<endl;
		} else {
			cout<<"YES"<<endl;
		}
		for(int i=1;i<=n;i++)
		{
			g[i].clear();
		} 
	}
	return 0;
}

如果能用dfs改出来的大佬,私我,让傻孩子见见世面。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值