题目链接
简单讲一下题意:
给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;
}