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; }