Codeforces1205B Shortest Cycle 【Flyod求最小环】


传送门:Codeforces1205B


B. Shortest Cycle
time limit per test:1 second
memory limit per test:256 megabytes
input:standard input
output:standard output

You are given n integer numbers a1,a2,…,an. Consider graph on n nodes, in which nodes i, j (i≠j) are connected if and only if, ai AND aj≠0, where AND denotes the bitwise AND operation.

Find the length of the shortest cycle in this graph or determine that it doesn’t have cycles at all.

Input
The first line contains one integer n (1≤n≤105) — number of numbers.

The second line contains n integer numbers a1,a2,…,an (0≤ai≤1018).

Output
If the graph doesn’t have any cycles, output −1. Else output the length of the shortest cycle.

Examples
input
4
3 6 28 9
output
4
input
5
5 12 9 16 48
output
3
input
4
1 2 4 8
output
-1

Note
In the first example, the shortest cycle is (9,3,6,28).

In the second example, the shortest cycle is (5,12,9).

The graph has no cycles in the third example.



题意:
给定 n 个点,每个点有一个权值a[i],如果a[u]&a[v] != 0,那么就可以在(u,v)之间连一条边,求最后图的最小环(环由几个点构成)


题解:
逻辑运算 & 是二进制下的运算,题目给的每个权值 a[i] 的范围最大是1018,即二进制下最多64位
如果64位中有某一位的1的出现数大于 2 了,那么很明显,最小环就是3(该位循环)。
换个说法,在最坏的情况下,给出了 n 个数,其中有超过 128 个不为 0 的数,那么答案一定是3(因为当有128个不为0的数时,64位每一位的个数都是2,只要再随便来个不为0的数,都会出现大于2的位数,构成 3 的环)

所以我们只要考虑不为 0 的数的个数小于130的情况就行了,这个时候 n 就很小了,可以用dfs搜索或者用Flyod求最小环。

我之前并不会Flyod求最小环,所以去学了一下。

Flyod求最小环:
令 e(u,v) 表示 u,v之间的连边,min(u,v)表示去掉u,v之间连边后,u,v之间最短路径;
所以最小环就是 e(u,v)+min(u,v)



AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#define ll long long
#define inf 1e8+10
using namespace std;
const int N=110000;
int n;
ll a[N];
int dis[250][250],mp[250][250];
void build()
{
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=n;j++)
    {
      if((a[i]&a[j])&&i!=j)
        dis[i][j]=mp[i][j]=1;
      else
        dis[i][j]=mp[i][j]=inf;
    }
  }
}
int flyod()
{
  int res=inf;
  for(int k=1;k<=n;k++)
  {
    for(int i=1;i<k;i++)
    {
      for(int j=i+1;j<k;j++)
        res=min(res,dis[i][j]+mp[i][k]+mp[k][j]);//假设k再i,j连边之间
    }
    for(int i=1;i<=n;i++)
    {
      for(int j=1;j<=n;j++)
        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);//去掉连边后的最短路
    }
  }
  return res;
}

int main()
{
  ll x;
  int cnt=0;//不为0的数的个数
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  {
    scanf("%I64d",&x);
    if(x!=0) a[++cnt]=x;
  }
  n=cnt;
  if(n>=130) printf("3\n");
  else
  {
    build();
    int res=flyod();
    if(res>n) printf("-1\n");//最小环大于n,即不可构造
    else printf("%d\n",res);
  }
  return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值