2018蓝桥杯算法c++/javaA、B、C组 题解 省赛题目在线测评 解题思路

写在前面:

大家好呀!~下面每个标题都是超链接,点击即可直达此题蓝桥杯官网在线评测系统(可提交),并在下面笔者的思路解题,每个cpp 都是通过了oj的,如果大家发现文重有错误或者更好的办法,十分欢迎告诉给我呀,一起学习嘻嘻。

字母阵列

主要思路 找到’L’ 然后 八个方向迭代即可。(emm 一定得是八条射线遍历,不是八皇后那种随机八个方向,所以在dfs中用for即可)

#include<string>
#include<algorithm>
#include<iostream>

using namespace std;
const int N = 110;
string s[N];
const string P = "LANQIAO";
int res = 0, n = 100;

void dfs(int col, int  row)
{
    for (int x = -1; x <= 1; ++ x) {    //九个方向 只访问八个
        for (int y = -1 ; y <= 1; ++ y) {
            if (x == 0 && y == 0) continue;     // 不重复 访问自身

            int xi = x + col, yi = row + y;     //下一个位置
            int i = 1;  // 因为 L 已经 匹配了 所以从 'A' (1)开始
            for ( ; i <= 6; ++ i) {
                if (xi < 0 || yi < 0 || xi >= n || yi >= n  || s[xi][yi] != P[i] )   break; // 不符合 就退出
                xi += x, yi += y;   // 迭代
            }
            if (i == 7)  //正常遍历 完就++
                ++res;
        }
    }
}
int main()
{
  // cout << 41 <<endl;return 0;
    for (int i = 0; i < n; ++ i) {
        cin >> s[i];
    }
    for (int i = 0; i < n; ++ i) {
        for (int j = 0; j < n; ++ j) {
            if (s[i][j] == 'L') {       // 遇到 'L' 就遍历
                dfs( i, j);
            }
        }
    }
    cout << res << endl;
    return 0;
}

方格计数

主要思想:
1、因为圆和正方形的对称性,只用枚举第一象限满足条件的点即可,然后再乘以4.
2、因为枚举第一象限,所以只要保证右端点在圆中,那整个正方形就可以在圆中。(不包括x,y轴,不能构成正方形)

#include<iostream>
#include<cmath>
using namespace std;
const long long  N = 50000;

void f1()  // O(n^2) 暴力 枚举 第一象限圆中 每一个 右端点
{
    long long  res = 0;
    for (long long  i = 1 ; i <= N; ++ i) {
        for (long long  j = 1; j <= N && i * i + j * j <=  N *  N ; ++ j) {
            ++res;
        }
    }
    cout << res * 4 << endl;
}
void f2()       // O(n) 只枚举 一个  右端点 y 坐标 即可
{
    long long res =  0;
    for (long long i = 1; i <= N; ++ i) {
        long long x = sqrt(N * N  - i * i);     // 算出 <= xd的即是可选的    直接取 下整 即可 
        if ( x <= N && x >= 1)
            res += x;
    }
    cout << res  * 4 << endl;
}
int main()
{
    f2();   //7853781044
    return 0;
}

分数(找规律)

思路: 手写一些前几次的运算,可以发现:**分子每次乘以2+1,分子每次乘以2即可。

#include<iostream>
int main()
{
    int n  = 20;
    int  b = 1, suma  = 1;      
    for (int i = 0; i < n - 1; ++ i) {  
        suma = suma * 2 + 1;        // 发现规律  分母的和 每次 都是 上一次 *2 + 1
        b *= 2;         //分子 一直乘以2 即可
    }
    printf("%d/%d\n", suma, b);
    return 0;
}

星期一 (用自带计算器或excel)

在这里插入图片描述

#include <iostream>
int main()
{
  int n =36524; 
  std::cout << n / 7 << std::endl;
  return 0;
}

猴子分香蕉

#include<iostream>
using namespace std;            // 直接枚举 即可 

bool check(int i )
{
    int x = 1;	//从1 开始的余数
    while ( i ) {
        if (i < 5) return false;    // 小于 5 则 false
        if ( i % 5 != x) return false;
        if (  x == 0) return true;      
        i -= (i / 5 + i % 5);   
        x  = (x + 1) % 5;       // mod 5 的相加
    }
    return true;
}
int main()
{
    for (int i = 1;; ++ i) {
        if (check(i)) {
            cout << i << endl;
            break;
        }
    }
    return 0;
}

缩位求合

思路 题目意思:不断的累加字符串每一位,再将这个累加的num 转为字符串s,直到s只剩一位即可。利用 to_stirng 可以很好的解决 复杂度大概是O(nlongn)

#include<iostream>
#include<string>
using namespace std;
string s;

void work()     
{
    int num = 0;            //用一个 num 把 统计所有字符 总和
    for(auto ch : s){
        num += ch - '0';
    }
    s = to_string(num);     //再转化为字符串
}
int main()
{
    cin >> s;
    while (s.size() >= 2) {     //直到 只剩下 一个 字符
        work();
    }
    cout << s << endl;
    return 0;
}

回家路费

#include<iostream>
using namespace std;
	//模拟
int main()
{
    int date = 0;
    for (int i = 1, sum = 0; sum < 108; i += 2, ++date )   // 模拟即可
        sum += i;
    cout << date << endl;
    return 0;
}

乘积尾零

对每个元素一直除以 2 和 5统计2 和 5 的个数 取min 即是答案。
在这里插入图片描述

#include<iostream>
using namespace std;
int num2, num5;     // 2 和 5 的数量
int main()
{
  // cout <<31 <<endl;// return 0;
    int n = 10, x, temp;
    for (int i = 0; i < n * n; ++ i) {  // 输入一百 个 数据
        cin >> x;
        temp = x;
        while (x && x % 2 == 0) {       // 求2 的个数
            x >>= 1;        // 求出一个 少 一个 右移 即除以二
            num2 ++;
        }
        while (temp && temp % 5 == 0 ) {        //一直 求 5 的个数
            temp /= 5;      // 求出一个 少一个 除以5
            num5 ++;
        }
    }
    cout << min(num2, num5) << endl;    //31
    return 0;
}

倍数问题

此题是一个限制选择个数 体积恰好(k 的倍数)类型的 背包问题 可以采用直接加一维进行枚举 即 f[4][ N ]

  1. 直接 加一维 暴力 :
 for (int i = 1 ; i < n; ++ i) {  // O(N *N *3)  即 3 * 1e8 肯定是会超时的 
        for (int j = 3; j >= 1; -- j)
            for (int v =  0; v < k; ++v) {	
                f[j][v] = max(f[j][v], f[j - 1][(v - item % k + k) % k ] + item);
            }
    }
  1. 贪心优化
    因为 是要求k的 倍数 如果 有很多同余k 相等的数就没必要都枚举了, 我们只用保留 三个 (可能这三个数都是k的倍数,都最大)最大的即可
    因此 只用 3e3 * 3e3 * 4 就可以枚举完 ,1e7 的量级 是完全OK哒
    注意点: 因为是恰好为k倍数的体积 :所以一开始 只有f[0][0]合法=0,其余都为负无穷表示不合法。
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
constexpr int  N = 1e3 + 10;        // 因为 做了贪心 优化: 只要 mod k 下相等 的 至多最大的 三个数  故 1 e3
set<int, greater<int>> arr[N];  // 降序
int n, k;
int f[4][N];        // 前i 个数 中 任意选 , 最多 选 j 个  且 总和 是 k 的倍数 的 最大 总和 是 f[i][j][k]  (优化了 空间 i 这一维)

int main()
{
    cin >> n >> k;
    int x;
    for (int i = 0 ; i < n; ++ i) {
        scanf("%d", &x);
        arr[x % k ].insert(x);  //同mod 下相等 的 插入 ,类似 hash 表的方式
    }
    // 总和 是 k 的倍数  可理解 为 恰好 是 k 的倍数
    memset(f, -0x3f, sizeof f);     // 一开始 是 前0 个中 任意选      总和 肯定不能 是 1,2,3,4,5... 的倍数 即不合法
    f[0][0] =  0;           // 只有 选 0 个 且 体积 为 0 才合法
    for (int i = 0 ; i < k; ++ i) {
        int cnt = 0;
        for (auto item : arr[i]) {      // 枚举从小到大 枚举 每个    mod k 相等 的数

            for (int j = 3; j >= 1; -- j)   //优化了空间 从后往前枚举
                for (int v = 0; v < k ; ++ v) {
                    f[j][v] = max(f[j][v], f[j - 1][(v - item % k + k) % k] + item );  //下标 必须是正数 所以 加k
                }
            if (++cnt == 3) break; //只要前三大的 数 即可

        }
    }
    cout << f[3][0] << endl;        //选了三个  mod k 为0 即 是 k 的 倍数
    return 0;
}

耐摔指数

类似于限制个数的背包选择最优解问题 ,但是又不一致,因为这个并不是简单的前i个钟选j个,而是枚举每一层 枚举每个选择 择出最优解。 因此这更像是 二维限制的背包问题。 手机个数 和 选择楼层 都算是体积 。而手机个数很小。
因此 可以尝试打表发现规律。 会发现就是类似于 二维体积限制的状态转移
易错:不能先入为主的 认为,最优方案(这种贪心最好能证明出来再做)就是 二分查找 ,因为手机个数可能不够,所以得通过dp选出最优解。
思路:// 枚举 n 层选择方案, 再枚举 (1 ~ n )中每层方案 的最坏情况最小值,逐步递推即可

#include<iostream>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;
constexpr int N  = 10010;
int f[4][N];
int n;
void work(int num)      // 类似于 背包问题 每层中最坏情况 中 的 最小 次数  就是最坏运气 下 的 最佳 策略
{
    for (int i = 1; i <= n; ++ i) {         // 枚举 n 层选择方案
        int ans =  INT_MAX;
        for (int j = 1; j <= i ; ++ j) {        // 从 1 开始 枚举 这层 的选择方案
            // 分摔 坏  和没摔坏 两种 情况 考虑   ,坏了 手机数 -1 ,层数 -1, 好的 话  手机数不变 则只有测 上面的剩下的层数
            int maxv = max(f[num - 1][j - 1], f[num][ i - j ] ) + 1;     // 这一次 本身 的 1 也得加上
            ans  = min(ans, maxv);
        }
        f[num][i] = ans;
    }
}
int main()          // 分情况 考虑  到某一层 摔坏了 和 没摔坏
{
    cin >> n;
    for (int i = 1; i <= n ; ++ i )  f[1][i] = i;
    work(2);
    work(3);
    cout << f[3][n] << endl;
    return 0;
}

乘积最大

思路 : 双指针 + 贪心 :主要难点 : 分类讨论 时,尽可能的把 各种方式的写法统一起来,这样会好些很多
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using LL = long long ;
constexpr int N = 1e5 + 10, mod = 1000000009;

int arr[N], n, k;

int main()
{
    cin >> n >> k;
    for (int i = 0 ; i < n; ++ i) scanf("%d", &arr[i]);
    sort(arr, arr + n);
    int l = 0, r = n - 1, sign = 1;     
    LL res  = 1;
    if (k & 1) {	//只用讨论是不是奇数 即可 只要是偶数计算 方法就一样
        res = arr[ r --];
        k -- ;
        if (res < 0)        // 全为 负数 我们就只要绝对值 更小的  也就是    负数 更大的
            sign =  -1;         //也可以直接 在这里 直接从右端 开始 乘 再输出 结束。 
    }

    while (k) {     // 双指针 两端遍历
        LL x = (LL)arr[l] * arr[l + 1],  y = (LL)arr[r] * arr[r -  1];      // 乘积过程 过程 可能 1e10 得转LL
        if (x * sign > y * sign) {
            res = x % mod * res % mod;
            l += 2;
        }
        else {
            res = y % mod * res % mod;
            r -= 2;
        }
        k -= 2;
    }
    cout << res << endl;
    return 0;
}

次数差

思路:因为s最长只有 1000,且都是字母, 所以 直接用unordered_map 统计 可以规避掉 0次数的处理然后直接算max 和min 即可

#include<iostream>
#include<unordered_map>
using namespace std;
unordered_map<char, int> ha;        // 直接用 hash表 做 这样就就 不用考虑 没有 输入过的  字母了
string s;
int main()
{
    cin >> s;
    for (auto ch : s)
        ha[ch ] ++;
    int maxv = 0, minv = 1001;
    for (auto &x : ha) {
        maxv = max(x.second, maxv);
        minv = min(x.second, minv);
    }
    cout << maxv - minv << endl;
    return 0;
}

等腰三角形

思路 :找规律 1 为 中点 底边 长为 2 * n + 1

#include<iostream>
#include<string>
using namespace std;
constexpr int N = 310;
int n ;
char s[N][N];
                // 枚举 从上到下 从左到右 再 从下到上 枚举即可
int main()
{
    cin >> n;
    for (int i = 1 ; i < N; ++i)        //便于 输出 先全部 赋 为 '.'
        for (int j = 1; j < N; ++ j)
            s[i][j] = '.';
    string num;
    for (int i = 1; i <= 10 * n; ++ i) {        // 实际上 应该是 2-3倍 左右的 n 但要细算边界   干脆开了10倍
        for (auto a : to_string(i))
            num += a;
    }
    int i = 1, j = n, k =  0;           // 从  (1,n ) 中点开始枚举
    for (; i <= n; ++i, --j) {          // i-1 ,j + 1, 最后 i 为 n + 1, j 为 -1 
        s[i][j] = num[k ++];
    }
    for (-- i, ++j, --k; j <= 2 * n - 1; ++ j ) {  //让 i -1 ,j + 1, k -1 即可  ,最后 j 为 2 *n - 1
        s[i][j] = num[k ++ ];
    }
    for (--j, --k; i > 1; -- i, --j)    // j - 1 , k - 1 
        s[i][j] = num[k ++];
    for (int i = 1; i <= n; ++ i) { 
        for (int j = 1; j <= n + i - 1; ++ j)  //注意到 每行 都是 只有 n + i - 1 个
            cout << s[i][j] ;
        puts("");
    }
    return 0;
}

递增三元组

思路 :1. 暴力 枚举 a中每个元素,每次枚举b中大于a[i]的,并在b中枚举大于b[j]的。三重循环 O(n ^ 3) 1e15 肯定超时了
2. 顺着 这个思路 用乘法原理来做 ,a 和c 中的元素 是独立的, 因此我们对 a,b,c 都排序b 可排 可不排,排序之后可以在枚举时做优化(文中没做),但复杂度都是O(nlongn
3. 也可以用双指针(滑动窗口)来优化,写法是类似的
枚举 b中每个元素 , 在a中找 小于 <b[i] 元素的数量,再在c中找>b[i]的数量相乘即可 再求和即可。 例 :
在这里插入图片描述

#include<algorithm>
#include<iostream>
using namespace std;
using LL = long long ;
constexpr int N = 1e5 + 10;
int a[N], b[N], c[N];
int n;
int main()
{
    cin >> n;       //下标从 1  开始 的好处 是 : 下标 的位置 就 是 元素 的个数 比如 a[1] 1 则也可以代表 有一个元素
    for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    for (int i = 1; i <= n; ++i) scanf("%d", b + i);
    for (int i = 1; i <= n; ++i) scanf("%d", c + i);
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    sort(c + 1, c + 1 + n);
    LL res =  0;  // 因为 最大 可能 是 1e5 * 1e5 * 1e5 会爆int
    for (int i = 1; i <= n; ++ i) {
        int na = lower_bound(a + 1, a + n + 1, b[i]) - a - 1 ;   // 返回 左边 第一个 >= b[i] 的位置  -1 个 则是 我们需要的 < b[i] 的位置
        int nc = upper_bound(c + 1, c + n + 1, b[i]) - c ;      // 返回 左边 第一个 > b[i] 的位置   正好 是我们想要的位置 
        res += (LL) na * ( n - nc  + 1);    // 记得转 LL    //  nc   是包含在内 的 所以   n - nc +1
    }
    cout << res << endl;
    return 0;
} 

付账问题

思路:贪心,从实际出发,有些人 的钱少,有些人正好,有些人多,为了保证方差尽可能小,显然得让这三种人 出的钱尽可能接近。
则: 不够 平均数 的全出够的人都出平均数即可 (平均数’,已经去除过不够的人 的平均数)。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

constexpr int  N = 5e5 + 10;
double arr[N], e;       // e 是  s/n 他们本来就要出的平均值 
int n;      
double s;

int main()
{
    cin >> n >> s;
    e =  double(s) / n;
    for (int i = 0 ; i < n ;  ++ i) scanf("%lf", &arr[i]);
    sort(arr, arr + n);     // 先排序
    double res =  0;
    int num = n;            // 剩余 未处理 的 数 的个数  一开始 自然 是 n
    for (int i = 0 ; i < n ; ++ i) {    // 从小到大 遍历
        if (arr[i] < s / num ) {                // 遇到小于 绝对值 的 就 直接 全要
            res += (arr[i] - e) * (arr[i] - e);     //方差计算
            s -= arr[i];        // 减去 arr[i] 这个数
            num --;
            continue;
        }
        //否则 说明后面的数  都 >= s / n  则 都出 s/n 这样他们的方差 更小 
        res += num * (s / num - e) * (s / num - e);
        break;
    }
    printf("%.4lf\n", sqrt(res / n));   // .4 就是 保留四位小数  四舍五入输出的
    return 0;
}

航班时间

思路: 二元一次方程组 直接 得到 res = (y1- x1 +y2 - x2) /2;
难点 在于 格式化 输入 和输出 。 用sscanf 做格式化输入printf做格式化输出会很方便, 还有 就是 因为除以2 可能会有精度丢失,而题目要求 输出的精度 是要精确要秒的 , 所以我们得至少转化为 秒 来计算
见代码:

#include <cstdio>
#include<iostream>
#include<string>
using namespace std;
string s1, s2;

int work(string &s)        
{
    if (s.back() != ')') s += " (+0)";  //把 他们都变成 一种 类型 即 都有 +d 这种类型来读
    int h1, m1, t1, h2, m2, t2, d = 0;
    sscanf(s.c_str(), "%d:%d:%d %d:%d:%d (+%d)", &h1, &m1, &t1, &h2, &m2, &t2, &d);  //用sscanf 把他们读出来
    return (h2 + d * 24) * 3600 + m2 * 60  + t2    -  ( h1 * 3600 + m1 * 60 + t1);  //返回 y - x 
}
int main()
{
    int t ;
    cin >> t;
    getline(cin, s1);
    while (t -- ) {
        getline(cin, s1);
        getline(cin, s2);
        int t = (work(s1) + work(s2)) / 2;      // 结果 则是 ((y1 - x1) + (y2 - x2) )/2 
        printf("%02d:%02d:%02d\n", t / 3600, t % 3600 / 60, t % 60);   //用 % 02d 来补齐两位
    }
    return 0;
}

螺旋折线

思路: 1.: 暴力点 1e9*1e9 1e18 肯定过不了。暴力边可以1e9 但也超时了。
2. 找规律:先算出每个点的 距离 。可以发现四个角点 与圈数的规律 。
3. 再由角点 到 每个边上的距离可以直接算,就可以O(1) 算出各个点的距离。
4. 再用线性规划的方式 每次都用两条直线划分出一个边(实际上是一个范围).
在这里插入图片描述

#include<iostream>
using namespace std;
using LL = long long ;
int main()
{
    int x, y;           //圈数 n 实际上就是 每条边 上 不变 的坐标 的 绝对值
    cin >> x >> y;
    if ( y > x && y >= -x) {        //在上边
        LL  n = y;          
        cout << (2 * n)* (2 * n - 1) + x + y << endl;
    }
    else if (y <= x && y > -x) {        // 在右边
        LL n = x ;
        cout << 2 * n * 2 * n + x - y;
    }
    else if (y <= -x && y < x + 1) { //在下边
        LL n = -y;
        cout << (2 * n )* (2 * n + 1) +  n - x << endl;
    }
    else {      //      在左边
        LL n = -x;
        cout << (2 * n - 1)* (2 * n - 1) + y -  (x + 1) << endl;
    }
    return 0;
}	

全球变暖

思路: 连通块模型 ,遍历 每一个连通块,统计每一个连通块中的 靠海陆地的数量,是否 与 连通块中陆地数量相同,相同 res就 ++ (并且这道题 不用处理边界,因为边界全是 ‘.’ ,我们只访问 是 ‘#’ 的结点,是不会越界的)

#include<iostream>
using namespace std;
constexpr int N  = 1010;
char s[N][N];
int n, res = 0, p = 0;   // p 代表 每个连通块 中 的 靠海 陆地 的数量 
const int dx[] = {1, -1, 0, 0},  dy[] = {0, 0, 1, -1};

int dfs(int x, int y)
{
    s[x][y] = 'x';      // 用 x 表示 这个 点已经 被访问过了  当然 也可以多开一个 bool 数组 来做这件事
    int re = 1;
    for (int i = 0 ; i < 4; ++ i) {
        int xi = x + dx[i], yi = y + dy[i];
        if (s[xi][yi] == '.') {         // 只要这个 结点 周围 原来 是 有 海洋 的 那就加1 
            ++p;
            break;
        }
    }
    for (int i = 0 ; i < 4; ++ i) {     // 如果 有相邻 的陆地 就继续统计
        int xi = x + dx[i], yi = y + dy[i];
        if (s[xi][yi] == '#')
            re += dfs(xi, yi);
    }
    return re;
}
int main()
{
    cin >> n;
    for (int i = 0 ; i < n ; ++ i)
        scanf("%s", s[i]);
    for (int i = 0 ; i < n; ++ i) {         // 遍历 每一个 连通块
        for (int j = 0 ; j < n; ++ j) {
            if (s[i][j] == '#') {       //访问陆地
                p = 0;
                if (dfs( i, j) == p) res ++ ;       //如果 这个连通块中 所有 陆地都 和 海 相邻 则会被淹没
            }
        }
    }
    cout << res << endl;
    return 0;
}

日志统计

思路:贪心 因为是对每一id的时间跨度统计点赞。所以把id的每一个点赞时间分出来统计即可。
1、考虑到 id 的值很小 (1e5) 因此 直接创建一个数组 把所有id装起来,并在里面像拉链法一样 拉出去一条 把时间ts从小到大 的序列
2、因为需要满足的时间区间是 d,我们维持一个首尾差距不大于d的区间 一直滑动检测每一个id的时间(ts 有序排列,由multi_set维持升序)即可。
3检测成功就输出 i 即id号 ,因为i是从小到大遍历 所以直接满足了升序输出

#include<iostream>
#include<set>
#include<queue>
using namespace std;
constexpr int N = 1e5 + 10;
multiset<int> id[N];        // 用multiset 懒得排序了 ,时间复杂度 O(n long n) 
int n, d, k, ti, ids;

int main()
{
    cin >> n >> d >> k;
    for (int i = 0 ; i < n; ++ i) {
        scanf("%d%d", &ti, &ids);
        id[ids].insert(ti);        // 把 t 按从小到大 的顺序 插入 到每一个 id 中
    }
    for (int i = 0; i < N; ++ i) {      
        queue<int> q;               // 用一个队列 来做 滑动窗口
        for (auto &a : id[i]) {         // 遍历 每个id 的 所有点赞时间
            q.push(a);
            if (q.back()  - q.front() >= d) q.pop();   // 维持一个 d 时间跨度 的窗口
            else if (q.size() >= k) {   //k 个 元素 所以 已经 足够 k 个 赞 了  直接输出,break 我们是从小到大 遍历 的,因此输出 也不必排序了。
                cout << i << '\n';
                break;
            }
        }
    }
    return 0;
}

小朋友崇拜圈

思路1: 暴力枚举 :枚举每一个 点 ,再一直遍历下去直到有环统计最大值O(n^2) 超时了,不过应该起码有一半分吧。
思路2 因为这题有一个很重要的特性,每个点都只会指向一个点,因此我们可以一个环一个环 的枚举(加一个is_loop判断即可)。这样是O(n)的完全可以过,还可以锦上添花一点,环外面指向环的点也不用访问
在这里插入图片描述

#include<iostream>
#include<unordered_set>
using namespace std;
constexpr int N = 1e5 + 10;
int e[N], n ;
unordered_set<int> vis;
int is_loop[N];
int main()
{
    cin >> n;
    int x ;                         //从这步操作 我们 可以 看出 每个结点               最多 只会 指向 一个结点
    for (int i = 1 ; i <= n ; ++ i) scanf("%d", &x),  e[i] = x;  // i 指向x
    int res = 0;

    for (int i =  1; i <= n ; ++i) {    
        if (is_loop[i]||is_loop[i]) continue;       // 是环中的点  肯定 不会再有更长的环

        vis.insert(i);      //寻找 第二次 访问过的点  那就是  环的起点
        int pos = i;
        for(;vis.find(e[pos]) == vis.end(); pos = e[pos])        //寻找环
            vis.insert(pos);

        int cnt = 1 ;
        auto t = *vis.find(e[pos]);       //先处理第一个元素
        is_loop[t] = true;

        for (int iter = e[t]; iter != t; iter = e[iter], ++cnt)     //把这个环中元素 都 置为 true,不再访问  并统计环中元素个数
            is_loop[iter] = true;
        res = max(res, cnt);        //取max
        vis.clear();            // vis 每次都针对一个环
    }
    cout << res << endl;
    return  0;
}

更新中…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值