SPOJ 1771&&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的模板就知道,它其实在选中一列之后,是会枚举列上的行值,

也就是说,该列(代表棋盘某一行)的每一个们置都会考虑到,不必担心无解。

DLX这个算法很巧妙啊,其实它只是一种高效的剪枝吧。妙妙妙,做过这题后才算真正懂得这个算法。

#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn=500;
const int maxnode=500*2500;
int ne;
int anst[maxn];
struct DLX
{
  int n , sz;                                                 // 行数,节点总数
  int S[maxn];                                                // 各列节点总数
  int row[maxnode],col[maxnode];                              // 各节点行列编号
  int L[maxnode],R[maxnode],U[maxnode],D[maxnode];            // 十字链表

  int ansd,ans[maxn];                                         // 解

  void init(int n )
  {
    this->n = n ;
    for(int i = 0 ; i <= n; i++ )
    {
      U[i] = i ;
      D[i] = i ;
      L[i] = i - 1;
      R[i] = i + 1;
    }
    R[n] = 0 ;
    L[0] = n;
    sz = n + 1 ;
    memset(S,0,sizeof(S));
  }
  void addRow(int r,vector<int> c1)
  {
    int first = sz;
    for(int i = 0 ; i < c1.size(); i++ ){
      int c = c1[i];
      L[sz] = sz - 1 ; R[sz] = sz + 1 ; D[sz] = c ; U[sz] = U[c];
      D[U[c]] = sz; U[c] = sz;
      row[sz] = r; col[sz] = c;
      S[c] ++ ; sz ++ ;
    }
    R[sz - 1] = first ; L[first] = sz - 1;
  }
  // 顺着链表A,遍历除s外的其他元素
  #define FOR(i,A,s) for(int i = A[s]; i != s ; i = A[i])

  void remove(int c){
    L[R[c]] = L[c];
    R[L[c]] = R[c];
    FOR(i,D,c)
      FOR(j,R,i) {U[D[j]] = U[j];D[U[j]] = D[j];--S[col[j]];}
  }
  void restore(int c){
    FOR(i,U,c)
      FOR(j,L,i) {++S[col[j]];U[D[j]] = j;D[U[j]] = j; }
    L[R[c]] = c;
    R[L[c]] = c;
  }
  bool dfs(int d){
    if(d >= ne ){
      ansd=d;
      for(int i=0;i<ne;i++){
      	int x=(ans[i]-1)/ne+1;
      	int y=(ans[i]-1)%ne+1;
      	anst[x]=y;
      }
      printf("%d",anst[1]);
      for(int i=2;i<=ne;i++)
      printf(" %d",anst[i]);
      printf("\n");
      return true;
    }
    // 找S最小的列c
    int c = R[0] ;
    for(int i=R[c];i;i=R[i]){ if(i>ne) break; if(S[i] < S[c]) c = i;}
    remove(c);
    FOR(i,D,c){
      ans[d] = row[i];
      FOR(j,R,i) remove(col[j]);
      if(dfs(d + 1)) return true;
      FOR(j,L,i) restore(col[j]);
    }
    restore(c);
    return false;
  }
  void solve(){
    dfs(0);
  }
};

DLX solver;

int puzzle[100][100];

int main(){
	int tmp;
	while(scanf("%d",&ne)!=EOF){
		memset(puzzle,0,sizeof(puzzle));
		for(int k=1;k<=ne;k++){
			scanf("%d",&tmp);
			if(tmp>0){
				for(int i=1;i<=ne;i++)
				puzzle[k][i]=puzzle[i][tmp]=-1;
				for(int i=1;k-i>0&&tmp-i>0;i++)
				puzzle[k-i][tmp-i]=-1;
				for(int i=1;k+i<=ne&&tmp+i<=ne;i++)
				puzzle[k+i][tmp+i]=-1;
				for(int i=1;k-i>0&&tmp+i<=ne;i++)
				puzzle[k-i][tmp+i]=-1;
				for(int i=1;k+i<=ne&&tmp-i>0;i++)
				puzzle[k+i][tmp-i]=-1;
				puzzle[k][tmp]=1;
			}
		}
		solver.init(6*ne-2);
		vector<int>columns;
		for(int i=1;i<=ne;i++){
			for(int j=1;j<=ne;j++){
				columns.clear();
				if(puzzle[i][j]>=0){
					columns.push_back(i);
					columns.push_back(ne+j);
					columns.push_back(ne*2+j-1+i);
					columns.push_back(ne*2+2*ne-1+ne-i+j);
					solver.addRow((i-1)*ne+j,columns);
				}
			}
		}
		solver.solve();
	}
	return 0;
}

  

摘http://www.cnblogs.com/jh818012/p/3252154.html

重复覆盖模板

const int maxn=360000;
const int maxc=500;
const int maxr=500;
const int inf=0x3f3f3f3f;
int L[maxn], R[maxn], D[maxn], U[maxn], C[maxn];
int S[maxc], H[maxr], size;
///不需要S域
void Link(int r, int c)
{
    S[c]++; C[size]=c;
    U[size]=U[c]; D[U[c]]=size;
    D[size]=c; U[c]=size;
    if(H[r]==-1) H[r]=L[size]=R[size]=size;
    else {
        L[size]=L[H[r]]; R[L[H[r]]]=size;
        R[size]=H[r]; L[H[r]]=size;
    }
    size++;
}
void remove(int c){
    for (int i=D[c]; i!=c; i=D[i])
        L[R[i]]=L[i], R[L[i]]=R[i];
}
void resume(int c){
    for (int i=U[c]; i!=c; i=U[i])
        L[R[i]]=R[L[i]]=i;
}
int h(){///用精确覆盖去估算剪枝
    int ret=0;
    bool vis[maxc];
    memset (vis, false, sizeof(vis));
    for (int i=R[0]; i; i=R[i])
    {
        if(vis[i])continue;
        ret++;
        vis[i]=true;
        for (int j=D[i]; j!=i; j=D[j])
            for (int k=R[j]; k!=j; k=R[k])
                vis[C[k]]=true;
    }
    return ret;
}

int ans;
void Dance(int k){                //根据具体问题选择限制搜索深度或直接求解。  A*算法,此处只求最优解
    if(k+h()>=ans) return;
    if(!R[0]){
        if(k<ans)ans=k;
        return;
    }
    int c=R[0];
    for (int i=R[0]; i; i=R[i])
        if(S[i]<S[c])c=i;
    for (int i=D[c]; i!=c; i=D[i]){
        remove(i);
        for (int j=R[i]; j!=i; j=R[j])
            remove(j);
        Dance(k+1);
        for (int j=L[i]; j!=i; j=L[j])
            resume(j);
        resume(i);
    }
    return ;
}

void initL(int x){///col is 1~x,row start from 1
    for (int i=0; i<=x; ++i){
        S[i]=0;
        D[i]=U[i]=i;
        L[i+1]=i; R[i]=i+1;
    }///对列表头初始化
    R[x]=0;
    size=x+1;///真正的元素从m+1开始
    memset (H, -1, sizeof(H));
    ///mark每个位置的名字
}

DLX 重复覆盖 template

 

精确覆盖模板

struct DLX
{
  int n , sz;                                                 // 行数,节点总数
  int S[maxn];                                                // 各列节点总数
  int row[maxnode],col[maxnode];                              // 各节点行列编号
  int L[maxnode],R[maxnode],U[maxnode],D[maxnode];            // 十字链表

  int ansd,ans[maxn];                                         // 解

  void init(int n )
  {
    this->n = n ;
    for(int i = 0 ; i <= n; i++ )
    {
      U[i] = i ;
      D[i] = i ;
      L[i] = i - 1;
      R[i] = i + 1;
    }
    R[n] = 0 ;
    L[0] = n;
    sz = n + 1 ;
    memset(S,0,sizeof(S));
  }
  void addRow(int r,vector<int> c1)
  {
    int first = sz;
    for(int i = 0 ; i < c1.size(); i++ ){
      int c = c1[i];
      L[sz] = sz - 1 ; R[sz] = sz + 1 ; D[sz] = c ; U[sz] = U[c];
      D[U[c]] = sz; U[c] = sz;
      row[sz] = r; col[sz] = c;
      S[c] ++ ; sz ++ ;
    }
    R[sz - 1] = first ; L[first] = sz - 1;
  }
  // 顺着链表A,遍历除s外的其他元素
  #define FOR(i,A,s) for(int i = A[s]; i != s ; i = A[i])

  void remove(int c){
    L[R[c]] = L[c];
    R[L[c]] = R[c];
    FOR(i,D,c)
      FOR(j,R,i) {U[D[j]] = U[j];D[U[j]] = D[j];--S[col[j]];}
  }
  void restore(int c){
    FOR(i,U,c)
      FOR(j,L,i) {++S[col[j]];U[D[j]] = j;D[U[j]] = j; }
    L[R[c]] = c;
    R[L[c]] = c;
  }
  bool dfs(int d){
    if(R[0] == 0 ){
      ansd = d;
      return true;
    }
    // 找S最小的列c
    int c = R[0] ;
    FOR(i,R,0) if(S[i] < S[c]) c = i;

    remove(c);
    FOR(i,D,c){
      ans[d] = row[i];
      FOR(j,R,i) remove(col[j]);
      if(dfs(d + 1)) return true;
      FOR(j,L,i) restore(col[j]);
    }
    restore(c);

    return false;
  }
  bool solve(vector<int> & v){
    v.clear();
    if(!dfs(0)) return false;
    for(int i = 0 ; i< ansd ;i ++ ) v.push_back(ans[i]);
    return true;
  }
};

DLX solver;

int main()
{
  int n,m;
  while(scanf("%d%d",&n,&m)!=EOF)
  {
    solver.init(m);
    int c , x;
    vector<int> c1;
    for(int i = 1; i<= n ; i ++ )
    {
      scanf("%d",&c);
      c1.clear();
      for(int j = 0 ; j < c ; j ++ ){scanf("%d",&x);c1.push_back(x);}
      solver.addRow(i,c1);
    }
    vector<int> ans;
    bool flag ;
    flag = solver.solve(ans);
    if(flag )
    {
      int size1 = ans.size();
      printf("%d",size1);
      for(int i = 0 ; i < size1;i ++ )
        printf(" %d",ans[i]);
      printf("\n");
    }
    else printf("NO\n");
  }
  return 0;
}

  

时间: 2024-10-13 00:26:10

SPOJ 1771&&DLX精确覆盖,重复覆盖的相关文章

最新版dlx模板(精确覆盖+重复覆盖)

以前的代码太挫了,重新整理dlx,学习HH把精确覆盖,重复覆盖整合在一起. 代码: struct DLX{ const static int maxn=20010; #define FF(i,A,s) for(int i = A[s];i != s;i = A[i]) int L[maxn],R[maxn],U[maxn],D[maxn]; int size,col[maxn],row[maxn],s[maxn],H[maxn]; bool vis[70]; int ans[maxn],cnt;

FZU 1686 DLX建图重复覆盖

Problem 1686 神龙的难题 Accept: 394    Submit: 1255 Time Limit: 1000 mSec    Memory Limit : 32768 KB Problem Description 这是个剑与魔法的世界.英雄和魔物同在,动荡和安定并存.但总的来说,库尔特王国是个安宁的国家,人民安居乐业,魔物也比较少.但是.总有一些魔物不时会进入城市附近,干扰人民的生活.就要有一些人出来守护居民们不被魔物侵害.魔法使艾米莉就是这样的一个人.她骑着她的坐骑,神龙米格

DLX精确覆盖与重复覆盖模板题

hihoCoder #1317 : 搜索四·跳舞链 原题地址:http://hihocoder.com/problemset/problem/1317 时间限制:10000ms 单点时限:1000ms 内存限制:256MB   描述 小Ho最近遇到一个难题,他需要破解一个棋局. 棋局分成了n行,m列,每行有若干个棋子.小Ho需要从中选择若干行使得每一列有且恰好只有一个棋子. 比如下面这样局面: 其中1表示放置有棋子的格子,0表示没有放置棋子. 对于上面这个问题,小Ho经过多次尝试以后得到了解为选

HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)

很久以前就看到的一个经典题,一直没做,今天拿来练手.街霸 给n<=25个角色,每个角色有 1 or 2 个版本(可以理解为普通版以及爆发版),每个角色版本可以KO掉若干人. 问最少选多少个角色(每个角色只能选一次),使得可以KO掉其他所有人(包括所有版本). 典型的DLX.前∑mode[i]列表示被KO的人版本,重复覆盖.后n列表示选了的人,精确覆盖. 即,在精确覆盖满足的前提下,完成重复覆盖,且使所选行最少. 据说这题可以转化成只用一种覆盖,或者是dfs+剪枝.这里就先这样吧. 加了好多注释,

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 =

HDU 2295.Radar (DLX重复覆盖)

2分答案+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 con

[ACM] HDU 2295 Radar (二分+DLX 重复覆盖)

Radar Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2593    Accepted Submission(s): 1012 Problem Description N cities of the Java Kingdom need to be covered by radars for being in a state of

HDU 3335 Divisibility(DLX可重复覆盖)

Problem Description As we know,the fzu AekdyCoin is famous of math,especially in the field of number theory.So,many people call him "the descendant of Chen Jingrun",which brings him a good reputation. AekdyCoin also plays an important role in th

[DLX重复覆盖] fzu 1686 神龙的难题

题意: 中文题 思路: 想到是一个重复覆盖的问题,然后就是最少放多少个能覆盖满. 建图的话就是先标记一下哪些点有怪物,最多就是n*m个怪物. 然后就是行. 行的话就看输入的x和y能框住多少的范围了. 然后四重循环遍历一遍建边就ok了. 代码: #include"stdio.h" #include"algorithm" #include"string.h" #include"iostream" #include"cma