BZOJ 1006 神奇的国度[弦图 MCS求完美消除序列]

chordal graph-弦图


1.定义

   弦图(chordalgraph)是指当无向图中任意长度大于3的环都至少有一个弦。所谓弦(chord)是指连接环中不相邻的两个点的边,如下图所示


大家都知道子图的概念,这里加入一个诱导子图(induced subgraph)的定义:


为了更好的理解弦图的概念,我们引进单纯点(simplicialvertex)的定义:N(v)表示与点v相邻的所有点集。一个点称为单纯点当{v}+N(v)的诱导子图为一个团({v}+N(v)的点集以及由这些点集在原图中的所有边的构成的图是一个团)如下图中的红圈圈住的点为单纯点:

chordal <wbr>graph-弦图

 

团:给定无向图G=(V,E)。如果U包含于V,且对任意u∈U,v∈U,存在边(u,v)∈E,则称U是G的完全子图。G的完全子图U是G的团当且仅当U不包含在G的更大的完全子图中。G的最大团是指G中所含顶点数最多的团。下图G中,子集{1,2}是G的大小为2的完全子图。这个完全子图不是最大团,因为它被G的更大的完全子图{1,2,5}包含。{1,2,5}是G的最大团。{1,4,5}和{2,3,5}也是G的最大团:




2.判断方法

   一个无向图是弦图当且仅当它有一个完美消除序列。而完美消除序列(perfect eliminationordering)是指一个点的序列(每个点出现且恰好出现一次)vi在{vi,vi+1,…,vn}的诱导子图中为一个单纯点。于是有了MCS算法,从n到1的顺序依次给点标号(标号为i的点出现在完美消除序列的第i个),设label[i]表示第i个点与多少个已标号的点相邻,每次选择label[i]最大的未标号的点进行标号,伪代码如下:

MCS(G, order)
{
   for all vertices v in G do
     Label[v] = 0

 

   for i from n to 1 do 
   {
      choose an unnumbered vertex v with largest Label
      order[v] = i

      for all unnumbered vertices u adjacent to v do 
         Label[u]++
   }
}

然后判断该序列是否是完美消除序列:对于每一个vi,找到{vi+1,vi+2,…,vn}中与vi相邻的标号最小的点vj,
判断其它{vi+1,vi+2,…,vn}中与vi相邻的点是否与vj相邻,若不相邻则不是完美消除序列。从而可以判断是不是弦图。

 

3.扩展
   弦图的方法有着很多经典用途:例如用最少的颜色给每个点染色使得相邻的点染的颜色不同,通过完美消除序列从后往前依次给每个点染色,给每个点染上可以染的最小的颜色;最大独立集问题,选择最多的点使得任意两个点不相邻,通过完美消除序列从前往后能选就选。

   我们再引入区间图的思想,chordal <wbr>graph-弦图会惊奇地发现它也是弦图。给定一些区间,定义一个相交图为每个顶点表示一个区间,两个点有边当且仅当两个区间的交集非空,如下图所示的区间图:

chordal <wbr>graph-弦图

很显然区间图一定是弦图。这是因为如果存在一个长度大于3的无弦环,刚Ii与Ii+1相交的部分pi一定严格递增或严格递减,以递增为例,即p1< pn。但由于第一个I1与最后一个In相交刚得出pn <p1产生矛盾。所以区间图一定是弦图。给定n个区间,要求选择最多的区间使得区间不互相重叠,这个问题就是区间图的最大独立集问题。




/*************************************************************************
	> File Name: 67.cpp
	> Author: wjzdmr
	> Mail: wjzdmr@gmail.com
	> Created Time: 2014年09月17日 星期三 20时03分40秒
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <list>
#include <deque>
#include <queue>
#include <cctype>
#include <map>
#include <set>
#include <bitset>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <limits>
#include <fstream>

using namespace std;

#define mem(A, X) memset(A, X, sizeof A)
#define pb(x) push_back(x)
#define mp(x,y) make_pair((x),(y))
#define vi vector<int>
#define all(x) x.begin(), x.end()
#define foreach(e,x) for(__typeof(x.begin()) e=x.begin();e!=x.end();++e)
#define sz(x) (int)((x).size())
#define sl(a) strlen(a)
#define rep(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i))
#define Rep(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i))
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define dbg(a) cout << a << endl;
#define fi first
#define se second
typedef long long int64;
int gcd(const int64 &a, const int64 &b) { return b == 0 ? a : gcd(b, a % b); }
int64 int64pow(int64 a, int64 b){ if (b == 0) return 1; int64 t = int64pow(a, b / 2); if (b % 2) return t * t * a; return t * t; }
const int inf = 1 << 30;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int MAX_N = 10005;

int n, m;
vi arr[MAX_N];
int R[MAX_N], SA[MAX_N], label[MAX_N];

priority_queue <pair<int, int> > heap;

void work()
{
    Rep(i, 1, n) {
        arr[i].clear();
    } 

    int u, v;
    Rep(i, 1, m) {
        scanf("%d%d", &u, &v);
        arr[u].pb(v);
        arr[v].pb(u);
    }

    fill(R + 1, R + n + 1, -1);
    fill(label + 1, label + n + 1, 0);

    Rep(i, 1, n) {
        heap.push(mp(0, i));
    }
    
    //MCS求弦图的完美消除序列
    for (int cnt = n; cnt >= 1; ) {
        int id = heap.top().se;
        heap.pop();

        if (R[id] != -1)
            continue;
        SA[cnt] = id;
        R[id] = cnt--;

        rep(i, 0, sz(arr[id])) {
            int u = arr[id][i];

            if (R[u] != -1) 
                continue;

            label[u]++;

            heap.push(mp(label[u], u));
        }
    }
}

void Color(int u) 
{
    rep(i, 0, sz(arr[u])) {
        int v = arr[u][i];
        if (label[v] == -1)
            continue;
        R[label[v]] = u;
    }

    for (int i = 1; label[u] == -1; ++i) {
        if (R[i] != u)
            label[u] = i;
    }
}

int Color_Graph()
{
    fill(label + 1, label + n + 1, -1);
    fill(R + 1, R + n + 1, -1);

    for (int i = n; i > 0; --i)
        Color(SA[i]);
    int ans = 0;
    Rep(i, 1, n)
        ans = max(ans, label[i]);
    return ans;
}
int main()
{
    while (cin >> n >> m) {
        work();
        cout << Color_Graph() << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值