zoukankan      html  css  js  c++  java
  • 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
    ", ++cas, solved() );
        }
        return 0;
    }
  • 相关阅读:
    Ajax请求ashx 返回 json 格式数据常见问题
    netbeans 优化设置
    Java基础static的探究
    mybatis中:selectKey返回最近插入记录的id
    Java map 详解
    Statement和PreparedStatement的异同
    css3 @media支持ie8用respond.js 解决IE6~8的响应式布局问题
    apply和call
    兼容性问题:backgroud-size支持IE8浏览器的方法
    json对象和json字符串之间的转化
  • 原文地址:https://www.cnblogs.com/GBRgbr/p/3265996.html
Copyright © 2011-2022 走看看