POJ_3740 Easy Finding ——精确覆盖问题,DLX模版

Easy Finding

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 18790   Accepted: 5184

Description

Given a M×N matrix AAij ∈ {0, 1} (0 ≤ i < M, 0 ≤ j < N), could you find some rows that let every cloumn contains and only contains one 1.

Input

There are multiple cases ended by EOF. Test case up to 500.The first line of input is MN (M ≤ 16, N ≤ 300). The next M lines every line contains N integers separated by space.

Output

For each test case, if you could find it output "Yes, I found it", otherwise output "It is impossible" per line.

Sample Input

3 3
0 1 0
0 0 1
1 0 0
4 4
0 0 0 1
1 0 0 0
1 1 0 1
0 1 0 0

Sample Output

Yes, I found it
It is impossible

Source

POJ Monthly Contest - 2009.08.23, MasterLuo

1、从矩阵中选择一行

2、根据定义,标示矩阵中其他行的元素

3、删除相关行和列的元素,得到新矩阵

4、如果新矩阵是空矩阵,并且之前的一行都是1,那么求解结束,跳转到6;新矩阵不是空矩阵,继续求解,跳转到1;新矩阵是空矩阵,之前的一行中有0,跳转到5

5、说明之前的选择有误,回溯到之前的一个矩阵,跳转到1;如果没有矩阵可以回溯,说明该问题无解,跳转到7

6、求解结束,把结果输出

7、求解结束,输出无解消息

Dancing Links用的数据结构是交叉十字循环双向链

而Dancing Links中的每个元素不仅是横向循环双向链中的一份子,又是纵向循环双向链的一份子。

因为精确覆盖问题的矩阵往往是稀疏矩阵(矩阵中,0的个数多于1),Dancing Links仅仅记录矩阵中值是1的元素。

#include <cstdio>
#include <cstring>
const int MAXR = 20;
const int MAXC = 310;
const int MAXN = MAXR * MAXC + MAXC;
const int INF = MAXR * 10;

int n, m;
int L[MAXN], R[MAXN], U[MAXN], D[MAXN];
int C[MAXN], O[MAXN], S[MAXN], H[MAXR];
int nodeNumber;

void init()
{
    for(int i=0;i<=m;++i)
    {
        L[i] = i - 1;
        R[i] = i + 1;
        U[i] = i;
        D[i] = i;
        C[i] = i;
        O[i] = 0;
        S[i] = 0;
    }
    L[0] = m;
    R[m] = 0;
    nodeNumber = m + 1;
    memset(H, 0, sizeof(H));
}

void insert(int i, int j)
{
    if(H[i])   //判断这一行中有没有节点
    {
        L[nodeNumber] = L[H[i]];    //如果有节点了,就添加一个节点,并把左指针指向第一个节点的未被更新的左指针,也就是新节点的左指针
        R[nodeNumber] = H[i];       //右指针指向该行第一个节点
        L[R[nodeNumber]] = nodeNumber;    //更新第一个节点的左指针
        R[L[nodeNumber]] = nodeNumber;    //更新前一个节点的右指针
    }
    else
    {
        L[nodeNumber] = nodeNumber;    //如果没有节点就添加一个节点,并把左右指针指向自己
        R[nodeNumber] = nodeNumber;
        H[i] = nodeNumber;             //标记为该行第一个节点
    }

    U[nodeNumber] = U[j];  //节点的上指针指向上面一个节点
    D[nodeNumber] = j;     //节点的下指针指向对应的列表头
    U[D[nodeNumber]] = nodeNumber;  //更新列表头的上指针指向当前节点
    D[U[nodeNumber]] = nodeNumber;  //更新上一个节点的下指针指向当前节点

    C[nodeNumber] = j;  //记录列号
    O[nodeNumber] = i;  //记录行号

    ++ S[j];         //S当中记录着每列节点的个数
    ++ nodeNumber;   //新建一个节点
}

void remove(int c)
{
    L[R[c]] = L[c];  //右节点的左指针指向原节点的左节点
    R[L[c]] = R[c];  //左节点的右指针指向原节点的右节点
    for(int i=D[c];i!=c;i=D[i])     //从该列往下第一个节点开始往下遍历
    {
        for(int j=R[i];j!=i;j=R[j]) //从当前行的第二个节点往右遍历,因为列已经被删除,所以第一个节点不用管
        {
            U[D[j]] = U[j];   //把前面删除的列上符合要求的行也删除
            D[U[j]] = D[j];
            -- S[C[j]];       //把相应列上对应的节点数也减少1个
        }
    }
}

void resume(int c)
{
    for(int i=U[c];i!=c;i=U[i])  //从该列最后一个节点往上遍历,不遍历列表头节点
    {
        for(int j=L[i];j!=i;j=L[j]) //从该行最后一个节点往左遍历,不遍历第一个节点
        {
            ++ S[C[j]];    //列上面恢复一个节点,节点数也+1
            D[U[j]] = j;   //恢复行
            U[D[j]] = j;
        }
    }
    R[L[c]] = c;  //最后恢复列
    L[R[c]] = c;
}

bool dfs(int k)
{
    if(!R[0])  //如果列表头上第一个节点的右指针为0,即所有列都被删除,则搜索完成
    {
        return true;
    }
    //因为要输出最优秀(最少的行)的答案,每次都要优先搜索列节点最少的列
    int count = INF, c;
    for(int i=R[0];i;i=R[i])  //从第一个列开始,直到右指针指向列头,即逐列遍历
    {
        if(S[i] < count)   //找到节点最少的列
        {
            count = S[i];  //count里面放最少的节点数
            c = i;         //把该列做标记
            if(1 == count) //该列节点,为最少允许的情况直接算是找到了,跳出
            {
                break;
            }
        }
    }
    remove(c);    //把该列和列上符合要求的行删除
    for(int i=D[c];i!=c;i=D[i])  //在被删除的列上,往下遍历
    {
        for(int j=R[i];j!=i;j=R[j]) //对应的行上往右遍历
        {
            remove(C[j]);  //如果行上有符合要求的列,删了
        }
        if(dfs(k+1)) //递归层数+1,深度搜索
        {
            return true;
        }
        for(int j=L[i];j!=i;j=L[j]) //从该行最后一个节点往左遍历,第一个节点不遍历
        {
            resume(C[j]);  //恢复之前删除的*行*
        }
    }
    resume(c); //递归跳出,恢复之前删除的列
    return false;
}

int main()
{
    int t;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        /*
        printf("L\tR\tU\tD\tC\tO\n");
        for(int i=0;i<=m;i++)
            {
               printf("%d\t",L[i]);
               printf("%d\t",R[i]);
               printf("%d\t",U[i]);
               printf("%d\t",D[i]);
               printf("%d\t",C[i]);
               printf("%d\t\n",O[i]);
            }
        */
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                scanf("%d", &t);
                if(t)
                {
                    insert(i, j);   //建立抽象十字链表
                }
            }
        }
        bool flag = true;
        for(int i=1;i<=m;++i)
        {
            if(S[i] == 0)  //如果有一列没有一个节点,直接失败
            {
                flag = false;
                break;
            }
        }
        if(flag && dfs(0))    //进入深度搜索
        {
            printf("Yes, I found it\n");
        }
        else
        {
            printf("It is impossible\n");
        }
    }
    return 0;
}
/*
6 7
0 0 1 0 1 1 0
1 0 0 1 0 0 1
0 1 1 0 0 1 0
1 0 0 1 0 0 0
0 1 0 0 0 0 1
0 0 0 1 1 0 1
*/

原文地址:https://www.cnblogs.com/caiyishuai/p/9019036.html

时间: 2024-10-31 23:30:13

POJ_3740 Easy Finding ——精确覆盖问题,DLX模版的相关文章

POJ 3047 Sudoku DLX精确覆盖

DLX精确覆盖.....模版题 Sudoku Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8336   Accepted: 2945 Description In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example, . 2 7 3 8 . . 1 . . 1 . .

POJ 3076 Sudoku DLX精确覆盖

DLX精确覆盖模版题..... Sudoku Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 4416   Accepted: 2143 Description A Sudoku grid is a 16x16 grid of cells grouped in sixteen 4x4 squares, where some cells are filled with letters from A to P (the fi

POJ 3074 Sudoku DLX精确覆盖

DLX精确覆盖.....模版题 Sudoku Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8336   Accepted: 2945 Description In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example, . 2 7 3 8 . . 1 . . 1 . .

ZOJ 3209 Treasure Map(DLX精确覆盖)

Your boss once had got many copies of a treasure map. Unfortunately, all the copies are now broken to many rectangular pieces, and what make it worse, he has lost some of the pieces. Luckily, it is possible to figure out the position of each piece in

POJ 3740 Easy Finding DLX

题意:给你一个0,1矩阵 ,求精确覆盖 解题思路: DLX 解题代码: 1 // File Name: poj3740.cpp 2 // Author: darkdream 3 // Created Time: 2014年10月04日 星期六 20时06分31秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #include<set> 9 #include<deque> 10 #i

zoj 3209.Treasure Map(DLX精确覆盖)

直接精确覆盖 开始逐行添加超时了,换成了单点添加 #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <vector> using namespace std; #define FOR(i,A,s) for(int i = A[s]; i != s; i = A[i]) #define exp 1e-8 const int MAX =

[DLX精确覆盖+打表] hdu 2518 Dominoes

题意: 就是给12种图形,旋转,翻折.有多少种方法构成n*m=60的矩形 思路: 裸的精确覆盖.就是建图麻烦 个人太挫,直接手写每一个图形的各种形态 须要注意的是最后的答案须要除以4 代码: #include"stdio.h" #include"algorithm" #include"string.h" #include"iostream" #include"queue" #include"map

[ACM] POJ 3740 Easy Finding (DLX模板题)

Easy Finding Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 16178   Accepted: 4343 Description Given a M×N matrix A. Aij ∈ {0, 1} (0 ≤ i < M, 0 ≤ j < N), could you find some rows that let every cloumn contains and only contains one 1.

SPOJ 1771&amp;&amp;DLX精确覆盖,重复覆盖

DLX的题,做过这题才算是会吧. 这道题转化成了精确覆盖模型来做,一开始,只是单纯的要覆盖完行列和斜线,WA. 后来醒悟了,不能这样,只要覆盖全部行或列即可.虽然如此,但某些细节地方很关键不能考虑到. 特别要注意的是 for(int i=R[c];i;i=R[i]){ if(i>ne) break; if(S[i] < S[c]) c = i;} 找最小值只能是在ne之前,为什么呢?因为我们要完全覆盖行.可行吗?可行.稍微留意一下DLX的模板就知道,它其实在选中一列之后,是会枚举列上的行值,