POJ_1084

    对于任意一个正方形,我们都至少要拿掉其中的一个火柴,于是如果构造一个行表示火柴、列表示正方形的矩阵,如果第i根火柴属于第j个正方形,那么g[i][j]=1,否则g[i][j]=0。这样这个问题就转化成了选取尽量少的行,使得每列至少有一个1,于是就可以用Dancing Links解决了。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXD 7710
#define INF 0x3f3f3f3f
int N, M, r[7][7], d[7][7], miss[70], ANS, vis[MAXD], CN;
int size, U[MAXD], D[MAXD], L[MAXD], R[MAXD], C[MAXD], S[MAXD], H[MAXD];
void prep(int n, int m)
{
    int i;
    for(i = 0; i <= m; i ++)
    {
        U[i] = D[i] = i;
        R[i] = i + 1, L[i + 1] = i;
        S[i] = 0;    
    }
    memset(H, -1, sizeof(H[0]) * (n + 1));
}
void insert(int r, int c)
{
    ++ size;
    C[size] = c, ++ S[c];
    D[size] = D[c], U[size] = c;
    U[D[c]] = size, D[c] = size;
    if(H[r] == -1) H[r] = L[size] = R[size] = size;
    else
    {
        L[size] = H[r], R[size] = R[H[r]];
        L[R[H[r]]] = size, R[H[r]] = size;
    }
}
void dor(int i, int &cnt)
{
    for(int j = 0; j < N; j ++) if(!miss[++ cnt]) r[i][j] = cnt;
}
void doc(int i, int &cnt)
{
    for(int j = 0; j <= N; j ++) if(!miss[++ cnt]) d[i][j] = cnt;    
}
int check(int x, int y, int n)
{
    int i;
    for(i = 0; i < n; i ++) if(!r[x][y + i]) return 0;
    for(i = 0; i < n; i ++) if(!d[x + i][y]) return 0;
    for(i = 0; i < n; i ++) if(!r[x + n][y + i]) return 0;
    for(i = 0; i < n; i ++) if(!d[x + i][y + n]) return 0;
    return 1;
}
void place(int x, int y, int n, int id)
{
    int i;
    for(i = 0; i < n; i ++) insert(r[x][y + i], id);
    for(i = 0; i < n; i ++) insert(d[x + i][y], id);
    for(i = 0; i < n; i ++) insert(r[x + n][y + i], id);
    for(i = 0; i < n; i ++) insert(d[x + i][y + n], id);    
}
void init()
{
    int i, j, k, x, cnt = 0;
    scanf("%d%d", &N, &M);
    memset(miss, 0, sizeof(miss));
    for(i = 0; i < M; i ++)
        scanf("%d", &x), miss[x] = 1;
    memset(r, 0, sizeof(r));
    memset(d, 0, sizeof(d));
    cnt = 0;
    for(i = 0; i < N; i ++)
        dor(i, cnt), doc(i, cnt);
    dor(N, cnt);
    cnt = 0;
    prep(2 * N * (N + 1), N * N * N);
    size = N * N * N;
    for(k = 1; k <= N; k ++)
        for(i = 0; i < N; i ++)
            for(j = 0; j < N; j ++)
                if(check(i, j, k)) place(i, j, k, ++ cnt);
    R[cnt] = 0, CN = cnt; 
}
int least()
{
    int i, j, k, ans = 0;
    memset(vis, 0, sizeof(vis[0]) * (CN + 1));
    for(i = R[0]; i != 0; i = R[i])
        if(!vis[i])
        {
            ++ ans;
            for(j = D[i]; j != i; j = D[j])
                for(k = R[j]; k != j; k = R[k]) vis[C[k]] = 1;
        }
    return ans;
}
void remove(int c)
{
    int i;
    for(i = D[c]; i != c; i = D[i])
        R[L[i]] = R[i], L[R[i]] = L[i];    
}
void resume(int c)
{
    int i;
    for(i = U[c]; i != c; i = U[i])
        R[L[i]] = L[R[i]] = i;
}
void dance(int dep)
{
    if(R[0] == 0)
    {
        ANS = std::min(ANS, dep);
        return ;    
    }
    if(dep + least() >= ANS) return ;
    int i, j, t = INF, id;
    for(i = R[0]; i != 0; i = R[i])
        if(S[i] < t) t = S[i], id = i;
    for(i = D[id]; i != id; i = D[i])
    {
        remove(i);
        for(j = R[i]; j != i; j = R[j]) remove(j);
        dance(dep + 1);
        for(j = L[i]; j != i; j = L[j]) resume(j);
        resume(i);
    }
}
void solve()
{
    ANS = INF;
    dance(0);
    printf("%d\n", ANS);
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t --)
    {
        init();
        solve();
    }
    return 0;    
}

 

 

 

posted on 2012-08-31 16:34  Staginner  阅读(719)  评论(2编辑  收藏  举报