• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
HaibaraAi
博客园    首页    新随笔    联系   管理    订阅  订阅

DLX AlgorithmX

AlgorithmX精确覆盖:
https://en.wikipedia.org/wiki/Knuth's_Algorithm_X
DLX的基础算法
https://zh.wikipedia.org/wiki/舞蹈链
论文:
https://arxiv.org/pdf/cs/0011047v1.pdf
中文版:
http://io.sqybi.com/dlxcn/#p11
精确覆盖的思想是选取n行中的x行,以至于m列都恰好一个1,可以求出所有解,也可以根据需要求最优解
为什么这玩意能解决精确覆盖呢,比如有nm的矩形让你用k个小矩形恰好覆盖,不相互覆盖
这里由于algorithmX解决的是所有列都恰好覆盖,好像不对啊,所以可以把n
m的矩形拉成一条直线,每个格子都是一列,这样就可以恰好覆盖所有列,也就是恰好覆盖n*m的矩形了
然后这里每行对应的列,就是原来每个小矩形对应的列,映射一下,构图,然后就可以用DLX解决了

#include <cstdio>
#include <memory>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <cassert>
#include <string>
#include <ctime>
#include <map>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cassert>
#include <iomanip> 
#include <set>
#include <iterator>  
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define req(i,a,b) for(int i=a;i>=b;i--)
#define rp(i,a) for(int i=head[a];i+1;i=edge[i].next)
#define cl(a,b) memset(a,b,sizeof a);
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mod 10007
const int inf = ~0u >> 2;
const ll INF = (1LL << 62) - 1;
double eps = 1e-12;
const int N = 200005 + 5;
const int M = 505;
int s[N];

int n, m;
int a[M][M];
class AlgorithmX {
private:
	bool dfs(vector<int> rows,vector<int> cols) {
		if (rows.size() == 0 && cols.size() == 0) {
			return true;
		}
		Content content(rows, cols);
		int minum = content.findMinColIndex(a);
		if (content.calColOneCount(minum) == 0)
			return false;
		vector<int> removeRows;
		vector<int> removeCols;
		for (int x = 0; x < rows.size(); x++) {
			int i = rows[x];
			if (a[i][minum] == 1) {
				tmpAnsRows.push_back(i);
				for (int y = 0; y < cols.size(); y++)
				{
					int j = cols[y];
					if (a[i][j] == 1)
						removeCols.push_back(j);
				}
				for (int y = 0; y < removeCols.size(); y++) {
					int j = removeCols[y];
					for (int x2 = 0; x2 < rows.size(); x2++) {
						int i2 = rows[x2];
						if (a[i2][j] == 1) {
							removeRows.push_back(i2);
						}
					}
				}
				unique(removeRows.begin(), removeRows.end());
				vector<int> lessRows;
				set_difference(rows.begin(), rows.end(), removeRows.begin(), removeRows.end(), insert_iterator<vector<int>>(lessRows,lessRows.begin()));
				vector<int> lessCols;
				set_difference(cols.begin(), cols.end(), removeCols.begin(), removeCols.end(), insert_iterator<vector<int>>(lessCols,lessCols.begin()));
				if (dfs(lessRows, lessCols)) {
					if (ansRows.size() == 0 || ansRows.size() < tmpAnsRows.size())
						ansRows = tmpAnsRows;
					cnt++;
				}
				tmpAnsRows.pop_back();
			}
		}
		return false;
	}
public:
	void init(int n) {
		for (int i = 0; i < n; i++)
			rows.push_back(i);
		for (int j = 0; j < n; j++)
			cols.push_back(j);
	}
	class Content {
	public:
		Content(vector<int> rows, vector<int> cols) {
			this->rows = rows;
			this->cols = cols;
		}
	private:
		vector<int> rows;
		vector<int> cols;
	public:
		int calColOneCount(int pos) {
			int num = 0;
			for (int j = 0; j < n; j++)
				num += a[pos][j];
			return num;
		}
		int findMinColIndex(int a[M][M]) {
			int nums[M] = { 0 };
			int minum = cols[0];
			for (int y = 0; y < cols.size(); y++)
			{
				int j = cols[y];
				nums[j] = 0;
				for (int x = 0; x < rows.size(); x++) {
					int i = rows[x];
					nums[j] += a[i][j];
				}
				if (nums[j] < nums[minum])
					minum = j;
			}
			return minum;
		}
	};
	void algorithmX() {
		dfs(rows,cols);
	}
	vector<int> rows;
	vector<int> cols;
	vector<int> tmpAnsRows;
	vector<int> ansRows;
	int cnt = 0;
}x;
int main() {
	int top = 0;
	cin >> n;	
	for(int i=0;i<n;i++)
		for (int j = 0; j < n; j++) {
			cin >> a[i][j];
		}
	x.init(n);
	x.algorithmX();
	return 0;
}

DLX:

#include <cstdio>
#include <memory>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <cassert>
#include <string>
#include <ctime>
#include <map>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cassert>
#include <iomanip> 
#include <set>
#include <iterator>  
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define req(i,a,b) for(int i=a;i>=b;i--)
#define rp(i,a) for(int i=head[a];i+1;i=edge[i].next)
#define cl(a,b) memset(a,b,sizeof a);
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mod 10007
const int inf = ~0u >> 2;
const ll INF = (1LL << 62) - 1;
double eps = 1e-12;
const int N = 505 + 5;
const int M = 35*35;
int s[N];

int n, m, k;
//int a[M][M];
int b[M*N+M];
class DLX {
private:
	const static int maxn = M*N+M;
public:
	int l[maxn], r[maxn], u[maxn], d[maxn], s[M];
	int row[maxn],col[maxn],head[N];
	int sz = 0;
	void init(int n,int mm) {
		cnt = 0;
		ans = inf;
		level = 0;
		sz = 0;
		for (int j = 0; j <= mm; j++)
		{
			row[j] = 0;
			col[j] = j;
			s[j] = 0;
			l[j] = j - 1;
			r[j] = j + 1;
			u[j] = d[j] = j;
			sz++;
		}
		sz--;
		l[0] = mm;
		r[mm] = 0;
		for (int i = 1; i <= n; i++)
			head[i] = -1;
	}
	void remove(int y) {//y is column index
		r[l[y]] = r[y];
		l[r[y]] = l[y];
		for (int i = d[y]; i != y; i = d[i]) {
			for (int j = r[i]; j != i; j = r[j]) {
				d[u[j]] = d[j];
				u[d[j]] = u[j];
				s[col[j]]--;
			}
		}
	}
	void resume(int y) {//y is column index
		for (int i = u[y]; i != y; i = u[i]) {
			for (int j = l[i]; j != i; j = l[j]) {
				d[u[j]] = j;
				u[d[j]] = j;
				s[col[j]]++;
			}
		}
		l[r[y]] = y;
		r[l[y]] = y;
	}
	void link(int rowIndex, int colIndex) {
		col[++sz] = colIndex;
		s[colIndex]++;
		d[sz] = d[colIndex];
		u[sz] = colIndex;
		u[d[sz]] = sz;
		d[u[sz]] = sz;
		if (head[rowIndex] == -1)
		{
			l[sz] = r[sz] = sz;
			head[rowIndex] = sz;
		}
		else {
			l[sz] = head[rowIndex];
			r[sz] = r[head[rowIndex]];
			l[r[sz]] = sz;
			r[l[sz]] = sz;
		}
		
	}
	bool dance() {
		if (r[0] == 0) {
			return true;
		}
		int minum = r[0];
		for (int i = r[0]; i != 0; i = r[i]) {
			if (s[i] < s[minum])
				minum = i;
		}
		if (s[minum] == 0) {
			return false;
		}

		level++;
		if (level >= ans)
		{
			level--;
			return false;
		}
		remove(minum);

		for (int i = d[minum]; i != minum; i = d[i]) {
			for (int j = r[i]; j != i; j = r[j]) {
				remove(col[j]);
			}
			bool flag = dance();
			for (int j = l[i]; j != i; j = l[j]) {
				resume(col[j]);
			}
			if (flag||level>=ans) {
				cnt++;
				ans = min(ans, level);
				break;
			}
		}

		resume(minum);
		level--;

		return false;
	}
	int getAns() {
		return ans;
	}
private:
	int cnt = 0;
	int ans = inf;
	int level = 0;
}dlx;
int main() {
	int t;
	//std::ios::sync_with_stdio(false);
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d%d", &n, &m, &k);
		int mm = n*m;
		dlx.init(k,mm);
		for (int p = 1; p <= k; p++) {
			int x, y, xx, yy;
			scanf("%d%d%d%d", &x, &y, &xx, &yy);
			
			for (int i = x + 1; i <= xx; i++)
			{
				for (int j = y + 1; j <= yy; j++) {
					dlx.link(p, (i - 1)*m + j);
				}
			}
		}
		dlx.dance();
		printf("%d\n", dlx.getAns()==inf?-1:dlx.getAns());
	}
	return 0;
}

另外附上Hadoop 上的DancingLinks:

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.examples.dancing;

import java.util.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A generic solver for tile laying problems using Knuth's dancing link
 * algorithm. It provides a very fast backtracking data structure for problems
 * that can expressed as a sparse boolean matrix where the goal is to select a
 * subset of the rows such that each column has exactly 1 true in it.
 * 
 * The application gives each column a name and each row is named after the
 * set of columns that it has as true. Solutions are passed back by giving the 
 * selected rows' names.
 * 
 * The type parameter ColumnName is the class of application's column names.
 */
public class DancingLinks<ColumnName> {
  private static final Log LOG = 
    LogFactory.getLog(DancingLinks.class.getName());
  
  /**
   * A cell in the table with up/down and left/right links that form doubly
   * linked lists in both directions. It also includes a link to the column
   * head.
   */
  private static class Node<ColumnName> {
    Node<ColumnName> left;
    Node<ColumnName> right;
    Node<ColumnName> up;
    Node<ColumnName> down;
    ColumnHeader<ColumnName> head;
    
    Node(Node<ColumnName> l, Node<ColumnName> r, Node<ColumnName> u, 
         Node<ColumnName> d, ColumnHeader<ColumnName> h) {
      left = l;
      right = r;
      up = u;
      down = d;
      head = h;
    }
    
    Node() {
      this(null, null, null, null, null);
    }
  }
  
  /**
   * Column headers record the name of the column and the number of rows that 
   * satisfy this column. The names are provided by the application and can 
   * be anything. The size is used for the heuristic for picking the next 
   * column to explore.
   */
  private static class ColumnHeader<ColumnName> extends Node<ColumnName> {
    ColumnName name;
    int size;
    
    ColumnHeader(ColumnName n, int s) {
      name = n;
      size = s;
      head = this;
    }
    
    ColumnHeader() {
      this(null, 0);
    }
  }
  
  /**
   * The head of the table. Left/Right from the head are the unsatisfied 
   * ColumnHeader objects.
   */
  private ColumnHeader<ColumnName> head;
  
  /**
   * The complete list of columns.
   */
  private List<ColumnHeader<ColumnName>> columns;
  
  public DancingLinks() {
    head = new ColumnHeader<ColumnName>(null, 0);
    head.left = head;
    head.right = head;
    head.up = head;
    head.down = head;
    columns = new ArrayList<ColumnHeader<ColumnName>>(200);
  }
  
  /**
   * Add a column to the table
   * @param name The name of the column, which will be returned as part of 
   *             solutions
   * @param primary Is the column required for a solution?
   */
  public void addColumn(ColumnName name, boolean primary) {
    ColumnHeader<ColumnName> top = new ColumnHeader<ColumnName>(name, 0);
    top.up = top;
    top.down = top;
    if (primary) {
      Node<ColumnName> tail = head.left;
      tail.right = top;
      top.left = tail;
      top.right = head;
      head.left = top;
    } else {
      top.left = top;
      top.right = top;
    }
    columns.add(top);
  }
  
  /**
   * Add a column to the table
   * @param name The name of the column, which will be included in the solution
   */
  public void addColumn(ColumnName name) {
    addColumn(name, true);
  }
  
  /**
   * Get the number of columns.
   * @return the number of columns
   */
  public int getNumberColumns() {
    return columns.size();
  }
  
  /**
   * Get the name of a given column as a string
   * @param index the index of the column
   * @return a string representation of the name
   */
  public String getColumnName(int index) {
    return columns.get(index).name.toString();
  }
  
  /**
   * Add a row to the table. 
   * @param values the columns that are satisfied by this row
   */
  public void addRow(boolean[] values) {
    Node<ColumnName> prev = null;
    for(int i=0; i < values.length; ++i) {
      if (values[i]) {
        ColumnHeader<ColumnName> top = columns.get(i);
        top.size += 1;
        Node<ColumnName> bottom = top.up;
        Node<ColumnName> node = new Node<ColumnName>(null, null, bottom, 
                                                     top, top);
        bottom.down = node;
        top.up = node;
        if (prev != null) {
          Node<ColumnName> front = prev.right;
          node.left = prev;
          node.right = front;
          prev.right = node;
          front.left = node;
        } else {
          node.left = node;
          node.right = node;
        }
        prev = node;
      }
    }
  }
  
  /**
   * Applications should implement this to receive the solutions to their 
   * problems.
   */
  public interface SolutionAcceptor<ColumnName> {
    /**
     * A callback to return a solution to the application.
     * @param value a List of List of ColumnNames that were satisfied by each
     *              selected row
     */
    void solution(List<List<ColumnName>> value);
  }
  
  /**
   * Find the column with the fewest choices.
   * @return The column header
   */
  private ColumnHeader<ColumnName> findBestColumn() {
    int lowSize = Integer.MAX_VALUE;
    ColumnHeader<ColumnName> result = null;
    ColumnHeader<ColumnName> current = (ColumnHeader<ColumnName>) head.right;
    while (current != head) {
      if (current.size < lowSize) {
        lowSize = current.size;
        result = current;
      }
      current = (ColumnHeader<ColumnName>) current.right;
    }
    return result;
  }
  
  /**
   * Hide a column in the table
   * @param col the column to hide
   */
  private void coverColumn(ColumnHeader<ColumnName> col) {
    LOG.debug("cover " + col.head.name);
    // remove the column
    col.right.left = col.left;
    col.left.right = col.right;
    Node<ColumnName> row = col.down;
    while (row != col) {
      Node<ColumnName> node = row.right;
      while (node != row) {
        node.down.up = node.up;
        node.up.down = node.down;
        node.head.size -= 1;
        node = node.right;
      }
      row = row.down;
    }
  }
  
  /**
   * Uncover a column that was hidden.
   * @param col the column to unhide
   */
  private void uncoverColumn(ColumnHeader<ColumnName> col) {
    LOG.debug("uncover " + col.head.name);
    Node<ColumnName> row = col.up;
    while (row != col) {
      Node<ColumnName> node = row.left;
      while (node != row) {
        node.head.size += 1;
        node.down.up = node;
        node.up.down = node;
        node = node.left;
      }
      row = row.up;
    }
    col.right.left = col;
    col.left.right = col;
  }
  
  /**
   * Get the name of a row by getting the list of column names that it 
   * satisfies.
   * @param row the row to make a name for
   * @return the list of column names
   */
  private List<ColumnName> getRowName(Node<ColumnName> row) {
    List<ColumnName> result = new ArrayList<ColumnName>();
    result.add(row.head.name);
    Node<ColumnName> node = row.right;
    while (node != row) {
      result.add(node.head.name);
      node = node.right;
    }
    return result;
  }
  
  /**
   * Find a solution to the problem.
   * @param partial a temporary datastructure to keep the current partial 
   *                answer in
   * @param output the acceptor for the results that are found
   * @return the number of solutions found
   */
  private int search(List<Node<ColumnName>> partial, SolutionAcceptor<ColumnName> output) {
    int results = 0;
    if (head.right == head) {
      List<List<ColumnName>> result = new ArrayList<List<ColumnName>>(partial.size());
      for(Node<ColumnName> row: partial) {
        result.add(getRowName(row));
      }
      output.solution(result);
      results += 1;
    } else {
      ColumnHeader<ColumnName> col = findBestColumn();
      if (col.size > 0) {
        coverColumn(col);
        Node<ColumnName> row = col.down;
        while (row != col) {
          partial.add(row);
          Node<ColumnName> node = row.right;
          while (node != row) {
            coverColumn(node.head);
            node = node.right;
          }
          results += search(partial, output);
          partial.remove(partial.size() - 1);
          node = row.left;
          while (node != row) {
            uncoverColumn(node.head);
            node = node.left;
          }
          row = row.down;
        }
        uncoverColumn(col);
      }
    }
    return results;
  }
  
  /**
   * Generate a list of prefixes down to a given depth. Assumes that the 
   * problem is always deeper than depth.
   * @param depth the depth to explore down
   * @param choices an array of length depth to describe a prefix
   * @param prefixes a working datastructure
   */
  private void searchPrefixes(int depth, int[] choices, 
                              List<int[]> prefixes) {
    if (depth == 0) {
      prefixes.add(choices.clone());
    } else {
      ColumnHeader<ColumnName> col = findBestColumn();
      if (col.size > 0) {
        coverColumn(col);
        Node<ColumnName> row = col.down;
        int rowId = 0;
        while (row != col) {
          Node<ColumnName> node = row.right;
          while (node != row) {
            coverColumn(node.head);
            node = node.right;
          }
          choices[choices.length - depth] = rowId;
          searchPrefixes(depth - 1, choices, prefixes);
          node = row.left;
          while (node != row) {
            uncoverColumn(node.head);
            node = node.left;
          }
          row = row.down;
          rowId += 1;
        }
        uncoverColumn(col);
      }
    }
  }
  
  /**
   * Generate a list of row choices to cover the first moves.
   * @param depth the length of the prefixes to generate
   * @return a list of integer arrays that list the rows to pick in order
   */
  public List<int[]> split(int depth) {
    int[] choices = new int[depth];
    List<int[]> result = new ArrayList<int[]>(100000);
    searchPrefixes(depth, choices, result);
    return result;
  }

  /**
   * Make one move from a prefix
   * @param goalRow the row that should be choosen
   * @return the row that was found
   */
  private Node<ColumnName> advance(int goalRow) {
    ColumnHeader<ColumnName> col = findBestColumn();
    if (col.size > 0) {
      coverColumn(col);
      Node<ColumnName> row = col.down;
      int id = 0;
      while (row != col) {
        if (id == goalRow) {
          Node<ColumnName> node = row.right;
          while (node != row) {
            coverColumn(node.head);
            node = node.right;
          }
          return row;
        }
        id += 1;
        row = row.down;
      }
    }
    return null;
  }
  
  /**
   * Undo a prefix exploration
   * @param row
   */
  private void rollback(Node<ColumnName> row) {
    Node<ColumnName> node = row.left;
    while (node != row) {
      uncoverColumn(node.head);
      node = node.left;
    }
    uncoverColumn(row.head);
  }
  
  /**
   * Given a prefix, find solutions under it.
   * @param prefix a list of row choices that control which part of the search
   *               tree to explore
   * @param output the output for each solution
   * @return the number of solutions
   */
  public int solve(int[] prefix, SolutionAcceptor<ColumnName> output) {
    List<Node<ColumnName>> choices = new ArrayList<Node<ColumnName>>();
    for(int i=0; i < prefix.length; ++i) {
      choices.add(advance(prefix[i]));
    }
    int result = search(choices, output);
    for(int i=prefix.length-1; i >=0; --i) {
      rollback(choices.get(i));
    }
    return result;
  }
  
  /**
   * Solve a complete problem
   * @param output the acceptor to receive answers
   * @return the number of solutions
   */
  public int solve(SolutionAcceptor<ColumnName> output) {
    return search(new ArrayList<Node<ColumnName>>(), output);
  }
  
}
posted @ 2017-03-06 22:26  HaibaraAi  阅读(336)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3