码迷,mamicode.com
首页 > 其他好文 > 详细

并查集初步题目(2)

时间:2015-11-13 20:55:41      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:

 

  • vector和邻接表
  • 并查集的一个很风骚的技巧

 

一. vector数组操作

包含vector头文件

声明:vector<type> name;

方法:

  1. 加入一个元素至最后:vec.push_back(val);
  2. 清空数组中的所有元素:vec.clear();
  3. 访问其中元素:vec.at(i);
  4. vector中元素的个数:vec.size();
  5. 指定当前vector内元素个数:vec.resize(n); 会保留前n个元素。

邻接表是一种储存图的方式,通常使用链表或者vector可变长数组实现。由表头节点表节点组成,图中的每一个顶点都在邻接表中为一个表头节点

邻接表与普通数组储存图的好处在于节约了空间与时间。

如果要遍历一个N个节点的图,数组需要两层for每层N次,事实上一个点并不一定与其它所有点都相邻,进行了无用的判断,还耗费了空间。

如果使用邻接表,使用vector类,可以使速度加快,空间变小,邻接表只储存于表头节点相邻的顶点,因此少了不必要的空间和遍历。

题目:

Hungar个人很喜欢曹操这个人,所以这次有机会穿越到三国时代,他想帮助曹操打赢赤壁之战。 
但是他去晚了,当他穿越到那的时候发现大火已经在蔓延了,所以他能做的就是马上告诉曹操把已经着火的船的锁链(船与船相连都是靠锁链达到的)给破坏掉使火不会蔓延到附近的船只。 

现在告诉你曹操一共有N艘船,M条铁链,船只编号从0开始到n-1。 
然后再告诉你依次着火的船只编号,问舍弃那艘船以后,剩下的船只能形成几个连通块(只要是被铁链连在一起的全部船只,就算一共连通块)。 
船与船之间可能存在多条锁链。 

Input

输入包括第一行两个整数,N(1 <= N <= 2M)和M(1 <= M <= 200,000)。 
接下来M行,每行包括两个整数x和y(x != y),分别表示编号为x和y的船只被一根锁链连起来。 
再接下来一个正整数T表示着火船只的数量。 
接下来T行,每行包含一个整数z表示被烧船只的编号,编号不会出现一样的,也就是说已经被烧的船只不会再去烧它。 

Output

输出z+1个数,第一行为着火前这些船的连通块数,后z行表示每次依次烧掉一只船后,剩下的连通块数。 

Sample Input

8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7

Sample Output

1
1
1
2
3
3

由于题目中顶点的数量高达400000,所以使用邻接表储存图,然而题目要求的操作是每次去掉一个顶点,判断一次联通块数量,可以每次都去掉一个点,进行一次图遍历。复杂度太高,舍弃。技巧:反向操作,从一个已有的并查集中去除一个点非常难,但是加入一个点很简单,倒序将点加入,每次遍历加入的顶点的邻接表。最后倒序输出即为答案。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#define MAX 400010
using namespace std;

vector<int> vis[MAX];
int ans[MAX];
int rec[MAX];
bool node[MAX];
int fa[MAX];
int N,M;

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void init()
{
    memset(node,0,MAX * sizeof(bool));
    for (int i = 0; i < N; ++i) fa[i] = i;
    for(int i = 1 ; i <= MAX ; i++) vis[i].clear();
    
    return ;

}


int main()
{
    while(~scanf("%d%d",&N,&M))
    {

        init();
        int a , b;
        for (int i = 1; i <= M ; ++i)
        {
            scanf("%d%d",&a,&b);
            vis[a].push_back(b);
            vis[b].push_back(a);
            
        }
        ///////////////////////////////////
        int case_num;
        scanf("%d",&case_num);
        int nodenum = N;
        for (int i = case_num - 1; i >= 0; i--)
        {
            scanf("%d",&rec[i]);
            node[rec[i]] = 1;
            nodenum--;
        }
        int key = 0;
        ans[key] = nodenum;
        /////////////////////////
        for (int i = 0; i < N; ++i)
        {
            a = i;
            for (int j = 0; j < vis[a].size(); ++j)
            {
                b = vis[a].at(j);
                if(node[a] || node[b]) continue;
                if(find(a) != find(b)) {find(a) < find(b) ? fa[find(b)] = find(a) : fa[find(a)] = find(b);ans[key]--;}
            }
        }

        ///////////////////////
        
        for (int i = 0; i < case_num; ++i)
        {
             key++;
             ans[key] = ans[key - 1] + 1;
             int a = rec[i];
             node[a] = 0;
             for (int j = 0; j < vis[a].size(); ++j)
             {
                 b = vis[a].at(j);
                 if(node[a] || node[b]) continue;
                 if(find(a) != find(b)) { find(a) < find(b) ? fa[find(b)] = find(a) : fa[find(a)] = find(b); ans[key]--;}
             }
        }
        for(int i = case_num ; i >= 0 ; i--)
            cout << ans[i] <<endl;


    }
    
    


    return 0;

}

 

 
 

并查集初步题目(2)

标签:

原文地址:http://www.cnblogs.com/ticsmtc/p/4963121.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!