无向图最小环——Floyd解法

题目链接
简单讲一下题意:
给n(<=1e5)个数,数字范围(1e18)任意两个数字之间如果经过与运算以后不为0则可以判为两点相连,给出最小环大小,如果没有则输出-1;
第一步将1e5变小,可以发现只要二进制的任意一位上面出现过3个乃至以上的1则只需输出3,大致算一下那么只有100个数字左右了可以跑Floyd了!!
由Floyd算法可以知道,在它运行到以k做中间节点时,前面经过所有点都比k小的节点之间的最短路径其实已经确定了。因为在k做中间节点时他的最大节点必须大于等于k。
因此每一次枚举一个中间节点我们都可以知道前面所产生的比k小的所有的最短路径。
而一个环(p->i->j->p)的长度为dis[i][j]+a[p][i]+a[j][p];我们每一次枚举dis[i][j]的长度再加上原本图上的两条边找到最小值就行了

#include <iostream>
#include <cstring>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int N = 1e5 + 5;
int n, cnt = 0,dis[200][200], p[200][200];
long long ans = N,a[N], b[N];
void floyd(void)
{
	for (int i = 1; i <= cnt; i++)//一定经过i点
	{
		for (int j = 1; j < i; j++)//枚举比i小的一个点
		{
			for (int k = j+1; k < i; k++)//
			{
				if(k!=j)
				ans = min((long long)dis[j][k] + p[k][i] + p[i][j], ans);//0x3f3f3f3f*3越int了
				//cout << ans<<endl;
			}
		}
		for (int j = 1; j <= cnt; j++)
		{
			for (int k = 1; k <= cnt; k++)
			{
				dis[j][k] = min(dis[j][k], dis[i][k] + dis[j][i]);
			}
		}
	}
}
int main()
{
	scanf("%d", &n);
	int v[63];
	memset(v, 0, sizeof(v));
	memset(dis, 0x3f3f3f3f, sizeof(dis));
	memset(p, 0x3f3f3f3f, sizeof(p));
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]);
		for (int j = 62; j > 0; j--)
		{
			if ((a[i] >> j)&1) v[j]++;
		}
	}
	for (int i = 1; i <= 62; i++)
	{
		if (v[i] > 2)
		{
			puts("3");
			return 0;
		}
	}
	for (int i = 1; i <= n; i++) if (a[i]) b[++cnt] = a[i];
	for (int i = 1; i <= cnt; i++)
	{
		for (int j = 1; j <= cnt; j++)
		{
			if (b[i] & b[j]) dis[i][j] = 1, p[i][j] = 1;
		}
	}
	//cout << ans << endl;
	floyd();
	//printf_s("%lld\n", ans);
	if (ans == N) ans = -1;
	cout << ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值