【代码】POJ 2942

来源:互联网 发布:张爱玲小说 知乎 编辑:程序博客网 时间:2024/06/05 17:31
// 题目来源:POJ 2942 ( Central Europe 2005 )// 题目模型:给定一个无向图G,求图中哪些点不能够在任何奇圈之内(奇圈即点数为奇数的圈)// 解题方法:对图求块,然后在各个块内二分染色判断// 特别注意:求块的时候在栈中要压边不能压点#include <cstdio>#include <string>using namespace std;bool map[1002][1002], ok;int next[2000002], p[2000002], g[2000002], h[1002], stack[2000002], dfn[1002], low[1002], c[1002][1002], code[1002];int t, sum, index, top, cnt, n, m;void link( int aa, int bb );    // 邻接表构建void tarjan( int i, int num );  // 求点双连通分量void color( int i );            // 在双连通分量内染色进行二分图判定void make( );                   // 根据得到的信息进行标记出不可行的点int main( ){    freopen( "input.txt", "r", stdin );    freopen( "output.txt", "w", stdout );    int aa, bb;    scanf( "%d%d", &n, &m );    while( n != 0 )    {        t = 1;          cnt = 0;        sum = 0;        memset( next, 0, sizeof( next ) );        memset( code, 0, sizeof( code ) );        memset( map, 0, sizeof( map ) );        memset( dfn, 0, sizeof( dfn ) );        memset( low, 0, sizeof( low ) );        memset( h, 0, sizeof( h ) );        memset( p, 0, sizeof( p ) );        memset( c, 0, sizeof( c ) );    // 数组初始化        for( int i = 1; i <= m; i++ )        {            scanf( "%d%d", &aa, &bb );            map[aa][bb] = map[bb][aa] = 1;  // 不可行边标记        }        for( int i = 1; i <= n; i++ )            for( int j = i+1; j <= n; j++ )                if( !map[i][j] ) link( i, j );  // 根据可行边构图        for( int i = 1; i <= n; i++ )            if( dfn[i] == 0 )  // 点未访问过            {                index = 0;  // 时间戳初始化                tarjan( i, 0 );  // 带边进行tarjan过程,求出所有的点双连通分量,并染色判定打标记            }        for( int i = 1; i <= n; i++ )             if( code[i] == 0 ) sum++;  // 该点不在任何奇圈之内        printf( "%d\n", sum );        scanf( "%d%d", &n, &m );    }    return 0;}void link( int aa, int bb ){    next[++t] = h[aa];    h[aa] = t;    g[t] = bb;    next[++t] = h[bb];    h[bb] = t;    g[t] = aa;}void tarjan( int i, int num ){    int j, e;  // j取点,e取边    dfn[i] = low[i] = ++index;      for( int k = h[i]; k; k = next[k] )    {        j = g[k];        if( (num^k) == 1 || dfn[j] > dfn[i] ) continue;  // 是指向父亲的边则放弃        stack[++top] = k;  // 当前边入栈        if( !dfn[j] )  // 若指向节点未被访问        {            tarjan( j, k );  // 带边进行递归            if( low[j] < low[i] ) low[i] = low[j];  // low值传递            if( dfn[i] <= low[j] )  // 判断当前是否产生了一个块            {                cnt++;  // 块的数量增加                do                {                    e = stack[top--];                      p[e] = p[e^1] = cnt;  // 将块内的边弹栈并打上块编号                }                while( e != k );                c[cnt][j] = 1; // j节点打上颜色标记                ok = 0;  // 是否找到奇圈初始化                color( j );  // 从j节点开始进行颜色标记                if( ok ) make( );  // 若找到奇圈则给块内节点标记可行            }        }        else            if( dfn[j] < low[i] ) low[i] = dfn[j];  // 修改low值    }}void color( int i ){    int j;    for( int k = h[i]; k; k = next[k] )    {        if( p[k] != cnt ) continue;  // 若当前边不在当前块内则忽视        j = g[k];        if( c[cnt][j] == 0 )  // 若目标节点未被染色        {            c[cnt][j] = 3 - c[cnt][i];  // 将其染上不同的颜色            color( j ); // 继续递归标记        }        else if( c[cnt][j] == c[cnt][i] ) // 出现相邻节点同色            ok = 1; // 奇圈寻找成功    }}void make( ){    for( int i = 1; i <= n; i++ )        if( c[cnt][i] != 0 ) code[i] = 1;  // 被染色过则标记可行}

原创粉丝点击