zoukankan      html  css  js  c++  java
  • noip模拟赛 道路分组

    分析:因为每一组编号都是连续的嘛,所以能分成一组的尽量分,每次加边后dfs判断一下1和n是否连通.有向图的判连通没有什么很快的方法,特别注意,并查集是错的!这个算法可以得到60分.

          事实上每一次都不需要从点1开始dfs,因为之前很多点都遍历到了,再从1开始会重复.如果新加的一条边的起点没有被访问过,这条边暂时是没用的,不需要再从1开始dfs,直接把这条边加进去就好了.如果这条边的起点已经被访问过了,那么从这条边的终点开始dfs就可以了,这样就节省了大量不必要的搜索,可以AC.

          正解是倍增+二分.还是这样一个贪心过程.只是不能一条一条边往里面加,太慢了,可以利用倍增的思想.每次加1条边,2条边,4条边......如果加2^i条边满足要求,加2^(i+1)条边不满足要求,就在2^i和2^(i+1)之间二分,看到底加多少条边,非常奇妙.感觉就像在树上跳一样,每次可以一步一步地跳,也可以先跳一大步,如果不行就跳一小步,如果可以就再跳一小步,这种方法可以加速每次+1的枚举.

    60分暴力:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n, m, vis[200010], T, head[200010],ans, to[500010], nextt[500010], tot = 1;
    
    struct node
    {
        int u, v;
    }e[500010];
    
    void add(int x, int y)
    {
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    void dfs(int u)
    {
        vis[u] = T;
        for (int i = head[u]; i; i = nextt[i])
        {
            int v = to[i];
            if (vis[v] != T)
            {
                vis[v] = T;
                dfs(v);
            }
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++)
            scanf("%d%d", &e[i].u, &e[i].v);
        for (int i = 1; i <= m; i++)
        {
            T++;
            add(e[i].u, e[i].v);
            dfs(1);
            if (vis[n] == T)
            {
                ans++;
                for (int j = 1; j <= n; j++)
                    head[j] = 0;
                tot = 1;
                add(e[i].u, e[i].v);
            }
        }
        printf("%d
    ", ans + 1);
    
        return 0;
    }

    正解:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n, m, vis[200010], T, head[200010], ans, to[500010], nextt[500010], tot = 1, cnt, pre[200010];
    
    struct node
    {
        int u, v;
    }e[500010];
    
    void add(int x, int y)
    {
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    void dfs(int u)
    {
        vis[u] = T;
        for (int i = head[u]; i; i = nextt[i])
        {
            int v = to[i];
            if (vis[v] != T)
            {
                vis[v] = T;
                dfs(v);
            }
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++)
            scanf("%d%d", &e[i].u, &e[i].v);
        int i = 1;
        while (i <= m)
        {
            add(e[i].u, e[i].v);
            pre[++cnt] = e[i].u;
            pre[++cnt] = e[i].v;
            vis[1] = T;
            if (vis[e[i].u] == T)
            {
                dfs(e[i].v);
                if (vis[n] == T)
                {
                    ans++;
                    T++;
                    for (int j = 1; j <= cnt; j++)
                        head[pre[j]] = 0;
                    tot = 1;
                    cnt = 0;
                }
                else
                    i++;
            }
            else
                i++;
    
        }
        printf("%d
    ", ans);
    
        return 0;
    }
  • 相关阅读:
    C语言 sprintf 函数 C语言零基础入门教程
    C语言 printf 函数 C语言零基础入门教程
    C语言 文件读写 fgets 函数 C语言零基础入门教程
    C语言 文件读写 fputs 函数 C语言零基础入门教程
    C语言 fprintf 函数 C语言零基础入门教程
    C语言 文件读写 fgetc 函数 C语言零基础入门教程
    C语言 文件读写 fputc 函数 C语言零基础入门教程
    C语言 strlen 函数 C语言零基础入门教程
    Brad Abrams关于Naming Conventions的演讲中涉及到的生词集解
    适配器模式
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7772051.html
Copyright © 2011-2022 走看看