HDU 3957 Street Fighter (最小支配集 DLX 重复覆盖+精确覆盖 )

DLX经典题型,被虐惨了……

建一个2*N行3*N列的矩阵,行代表选择,列代表约束。前2*N列代表每个人的哪种状态,后N列保证每个人至多选一次。

显然对手可以被战胜多次(重复覆盖),每个角色至多选择一次(精确覆盖)。

注意事项:

1.行数=∑每个人的模式数,之前我直接把行数当2*N了……但实际上也会有人只有一种模式的,也就是说实际行数小于等于2*N

2.建图的时候注意:这个人不光能覆盖他所战胜的某角色的某模式,还覆盖了他自己的所有模式(因为他不用战胜自己)。之前没注意这个问题,样例全成无解了orz……

3.处理精确覆盖和重复覆盖的先后顺序。如果优先处理精确覆盖,会把重复覆盖的一些行也删掉,这样前面可以重复覆盖的很多列也被当成了精确覆盖,显然不对了。所以应当先处理重复覆盖。恢复的时候遵循先删除的后恢复,后删除的先恢复

4.只要满足重复覆盖的条件即为一个可行解。

 

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <algorithm>



using namespace std;



const int MAXN = 50;

const int INF = 1 << 30;



int N;

int U[ (2*MAXN)*(3*MAXN) ], D[ (2*MAXN)*(3*MAXN) ];

int L[ (2*MAXN)*(3*MAXN) ], R[ (2*MAXN)*(3*MAXN) ];

int C[ (2*MAXN)*(3*MAXN) ];

int cnt[ 3*MAXN ];

bool mx[ 2*MAXN ][ 3*MAXN ];

bool vis[ 3*MAXN ];

bool vs[MAXN][2][MAXN][2];   //i的x模式能打败j的y模式

int modelN[MAXN];            //i有几个模式

int sum[MAXN];

int head;

int maxr, maxc;



void Remove( int c )    //重复覆盖删除列

{

    for ( int i = D[c]; i != c; i = D[i] )

    {

        R[ L[i] ] = R[i];

        L[ R[i] ] = L[i];

    }

    return;

}



void Resume( int c )    //重复覆盖恢复列

{

    for ( int i = D[c]; i != c; i = D[i] )

    {

        R[ L[i] ] = i;

        L[ R[i] ] = i;

    }

    return;

}



void ExRemove( int c )    //精确覆盖删除列+行

{

    int i, j;

    L[ R[c] ] = L[c];

    R[ L[c] ] = R[c];

    for ( i = D[c]; i != c; i = D[i] )

    {

        for ( j = R[i]; j != i; j = R[j] )

        {

            U[ D[j] ] = U[j];

            D[ U[j] ] = D[j];

            --cnt[ C[j] ];

        }

    }

    return;

}



void ExResume( int c )      //精确覆盖恢复列+行

{

    int i, j;

    R[ L[c] ] = c;

    L[ R[c] ] = c;

    for ( i = D[c]; i != c; i = D[i] )

    {

        for ( j = R[i]; j != i; j = R[j] )

        {

            U[ D[j] ] = j;

            D[ U[j] ] = j;

            ++cnt[ C[j] ];

        }

    }

    return;

}



bool build()

{

    head = 0;

    for ( int i = 0; i < maxc; ++i )

    {

        R[i] = i + 1;

        L[i + 1] = i;

    }

    R[maxc] = 0;

    L[0] = maxc;



    //列链表

    for ( int j = 1; j <= maxc; ++j )

    {

        int pre = j;

        cnt[j] = 0;

        for ( int i = 1; i <= maxr; ++i )

        {

            if ( mx[i][j] )

            {

                ++cnt[j];

                int cur = i * maxc + j;

                U[cur] = pre;

                D[pre] = cur;

                C[cur] = j;

                pre = cur;

            }

        }

        U[j] = pre;

        D[pre] = j;

        //if ( !cnt[j] ) return false;

    }



    //行链表

    for ( int i = 1; i <= maxr; ++i )

    {

        int pre = -1, first = -1;

        for ( int j = 1; j <= maxc; ++j )

        {

            if ( mx[i][j] )

            {

                int cur = i * maxc + j;

                if ( pre == -1 ) first = cur;

                else

                {

                    L[cur] = pre;

                    R[pre] = cur;

                }

                pre = cur;

            }

        }

        if ( first != -1 )

        {

            R[pre] = first;

            L[first] = pre;

        }

    }



    return true;

}



/****************以上DLX模板****************/



//估价函数:至少还要选几个人

int h()

{

    memset( vis, false, sizeof(vis) );

    int res = 0;

    for ( int c = R[head]; c <= maxr && c != head; c = R[c] )

    {

        if ( !vis[c] )

        {

            ++res;

            vis[c] = true;

            for ( int i = D[c]; i != c; i = D[i] )

                for ( int j = R[i]; j != i; j = R[j] )

                    vis[ C[j] ] = true;

        }

    }

    return res;

}



bool DFS( int dep, int limit )

{

    //A-star剪枝

    if ( dep + h() > limit ) return false;



    //只要前面满足重复覆盖的条件,即为可行解

    if ( R[head] > maxr || R[head] == head ) return true;



    int c, minv = INF;

    for ( int i = R[head]; i <= maxr && i != head; i = R[i] )

    {

        if ( cnt[i] < minv )

        {

            minv = cnt[i];

            c = i;

        }

    }



    for ( int i = D[c]; i != c; i = D[i] )

    {

        Remove(i);

        //注意处理重复覆盖和精确覆盖的顺序

        for ( int j = R[i]; j != i; j = R[j] )

            if ( C[j] <= maxr ) Remove(j);



        for ( int j = R[i]; j != i; j = R[j] )

            if ( C[j] > maxr ) ExRemove( C[j] );



        if ( DFS( dep + 1, limit ) )

        {

            //注意恢复精确覆盖和重复覆盖的顺序,这样恢复之后可以不必重新建图

            for ( int j = R[i]; j != i; j = R[j] )

                if ( C[j] > maxr ) ExResume( C[j] );



            for ( int j = R[i]; j != i; j = R[j] )

                if ( C[j] <= maxr ) Resume(j);



            Resume(i);  //之前忘了恢复i,死活TLE

            return true;

        }



        for ( int j = R[i]; j != i; j = R[j] )

            if ( C[j] > maxr ) ExResume( C[j] );

        for ( int j = R[i]; j != i; j = R[j] )

            if ( C[j] <= maxr ) Resume(j);

        Resume(i);

    }



    return false;

}



int solved()

{

    int l = 0, r = N;

    int ans;



    while ( l <= r )

    {

        int mid = ( l + r ) >> 1;

        if ( DFS( 0, mid ) )

        {

            r = mid - 1;

            ans = mid;

        }

        else l = mid + 1;

    }



    return ans;

}



void show()

{

    for ( int i = 0; i <= maxr; ++i )

    {

        for ( int j = 0; j <= maxc; ++j )

            printf( "%d", mx[i][j] );

        puts("");

    }

}



void init()

{

    memset( mx, false, sizeof(mx) );



    for( int i = 0; i < N; ++i )

    {

        for ( int x = 0; x < modelN[i]; ++x )

        {

            mx[ sum[i] + x ][ maxr + i + 1 ] = true;

            mx[ sum[i] + x ][ sum[i] ] = true;

            if ( modelN[i] > 1 )

            {

                mx[ sum[i] + x ][ sum[i] + 1 ] = true;

            }

            for ( int j = 0; j < N; ++j )

            {

                for ( int y = 0; y < modelN[j]; ++y )

                {

                    if ( vs[i][x][j][y] )

                    {

                        mx[ sum[i] + x ][ sum[j] + y ] = true;

                    }

                }

            }

        }

    }



    //show();

    return;

}



int main()

{

    //freopen( "in.txt", "r", stdin );

    //freopen( "out.txt", "w", stdout );

    int T, cas = 0;

    scanf( "%d", &T );

    while ( T-- )

    {

        memset( vs, false, sizeof(vs) );

        scanf( "%d", &N );

        maxr = 0;



        for ( int i = 0; i < N; ++i )

        {

            scanf( "%d", &modelN[i] );

            sum[i] = maxr + 1;

            for ( int j = 0; j < modelN[i]; ++j )

            {

                ++maxr;

                int K;

                scanf( "%d", &K );

                for ( int k = 0; k < K; ++k )

                {

                    int id, mode;

                    scanf( "%d%d", &id, &mode );

                    vs[i][j][id][mode] = true;

                }

            }

        }



        maxc = maxr + N;

        init();

        build();

        printf("Case %d: %d\n", ++cas, solved() );

    }

    return 0;

}

 

你可能感兴趣的:(tree)