POJ 3740 DLX

题意:给你一个01矩阵,然后求是否存在选择一些行,使得每一列的1的个数都为1。

思路:貌似朴素的DFS也可以,加点剪枝就可以过。这里贴个DLX的模版。

推荐博客:http://www.cppblog.com/notonlysuccess/archive/2009/07/10/89701.html

这里讲的很详细。

#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Max 2505
#define FI first
#define SE second
#define ll long long
#define PI acos(-1.0)
#define inf 0x3fffffff
#define LL(x) ( x << 1 )
#define bug puts("here")
#define PII pair<int,int>
#define RR(x) ( x << 1 | 1 )
#define mp(a,b) make_pair(a,b)
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,s,t) for( int i = ( s ) ; i <= ( t ) ; ++ i )

using namespace std;

#define N 5555

///DLX
int L[N] , R[N] , D[N] , U[N] ,S[N] , C[N] ,st[N] ;//S[] 表示这一列的点数。C[] 表示这个点位于那一列。
int n , m , num , head ;

void insert(int col , int pos){//在这一列插入序号为pos的点
    int now = col ;
    while(D[now] != col) now = D[now] ;
    D[now] = pos ;
    D[pos] = col ;
    U[pos] = now ;
    U[col] = pos ;
}

void init(){
    head = 0 ;
    R[head] = 1 ;L[head] = m ;
    for (int i = 1 ; i <= m ; i ++ ){//每一行的头指针
        if(i == 1)L[i] = head ;
        else L[i] = i - 1 ;
        if(i == m)R[i] = head ;
        else R[i] = i + 1 ;
        U[i] = i ;
        D[i] = i ;
        S[i] = 0 ;
        C[i] = i ;
    }
    num = m ;//已经插入m个节点
    int k ;
    for (int i = 1 ; i <= n ; i ++ ){
        mem(st ,0) ;
        for (int j = 1 ; j <= m ; j ++ ){
            scanf("%d",&k) ;
            if(!k)continue ;
            num ++ ;
            insert(j , num) ;
            if(st[0] == 0){//每行的第一个
                L[num] = num ; R[num] = num ;
            }else{
                L[num] = st[st[0]] ;
                R[num] = st[1] ;
                R[st[st[0]]] = num ;
                L[st[1]] = num ;
            }
            st[++st[0]] = num ;
            C[num] = j ;
            S[j] ++ ;
        }
    }
}

void remove(const 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]] ;
        }
    }
}

void resume(const 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]] ;
            U[D[j]] = j ;
            D[U[j]] = j ;
        }
    }
    L[R[c]] = c ;
    R[L[c]] = c ;
}

int dfs(const int &k){
    if(R[head] == head)return 1 ;
    int MX = inf ,c ;
    for (int t = R[head] ; t != head ; t = R[t]){//找出点最少的一列
        if(S[t] < MX){
            MX = S[t] ;
            c = t ;
        }
    }
    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))return 1 ;
        for (int j = L[i] ; j != i ; j = L[j]){
            resume(C[j]) ;
        }
    }
    resume(c) ;
    return 0 ;
}
int main() {
    while(cin >> n >> m){
        init() ;
        if(dfs(0))puts("Yes, I found it") ;
        else puts("It is impossible") ;
    }
    return 0 ;
}

你可能感兴趣的:(POJ 3740 DLX)