题目大意:有 n个点( n ≤ 1 0 5 n \leq10^5 n≤105),每个点有个点权,若 a i A N D a j a_i AND a_j aiANDaj != 0,则i,j之间有一条边(AND是位运算符中的与运算) ,问这个图的最小环是多大,若没有环输出 -1。
题解:首先若二进制某一位有超过2个数字在这一位不为0,那么答案就是3,否则,不为0的点不超过200个,可以暴力找最小环,这里用floyd算法 n 3 n ^ 3 n3(相同的算法原理可以用dijkstra优化到 n 2 ∗ l o g n n ^2 * logn n2∗logn)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int inf = 500;
int n;
typedef long long ll;
ll a[maxn];
int p[100];
int dis[300][300],mp[300][300],cnt,tmp[300];
int main() {
scanf("%d",&n);
cnt = 0;
for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
for(int i = 1; i <= n; i++)
for(int j = 60; j >= 0; j--)
if(a[i] >> j & 1) p[j]++;
bool f = false;
for(int i = 0; i <= 60; i++)
if(p[i] > 3) f = true;
if(f)
puts("3");
else {
for(int i = 1; i <= n; i++)
if(a[i]) tmp[++cnt] = i;
for(int i = 1; i <= cnt; i++)
for(int j = 1; j <= cnt; j++)
dis[i][j] = mp[i][j] = inf;
for(int i = 1; i <= cnt; i++)
for(int j = i + 1; j <= cnt; j++)
if(a[tmp[i]] & a[tmp[j]])
dis[i][j] = dis[j][i] = mp[i][j] = mp[j][i] = 1;
for(int i = 1; i <= cnt; i++) dis[i][i] = 0;
int ans = inf;
for(int k = 1; k <= cnt; k++) {
for(int i = 1; i < k; i++)
for(int j = i + 1; j < k; j++)
ans = min(ans,dis[i][j] + mp[i][k] + mp[k][j]);
for(int i = 1; i <= cnt; i++)
for(int j = 1; j <= cnt; j++)
dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
}
if(ans == inf) puts("-1");
else printf("%d\n",ans);
}
return 0;
}