DLX 重复覆盖 & IDA* -- Square Destroyer POJ - 1084

Square Destroyer POJ - 1084
在这里插入图片描述
在这里插入图片描述
题意:
给一个由(n+1)n2根火柴棒构成的边长为n正方形,每根火柴棒都有编号,已经删去了k根火柴棒,问至少再删多少根火柴棒可以破坏掉所有正方形。

思路:
开两个数组stick[][], square[][] (我用的是vector), stick[i]表示的是需要编号为i的木棒的正方形有哪些,square[i]表示的是编号为i的正方形需要的木棒有哪些。

exi[i]表示的是编号i的正方形是否存在:exi[i] == 0, 存在;每删去该正方形上的一根火柴,exi[i] - - 。这么做就解决了递归、回溯的问题。

观察一下木棒的编号规律,提前预处理出来正方形们以及木棒们(square,stick两个数组),开始搜索,找到一个还存在的正方形,枚举删除它的哪一根木棒,递归。为了节省时间,要找还存在的边长最小的正方形,这个在我们预处理square数组时,加入正方形的顺序就可以搞定。

估价函数:对于当前一个状态,枚举每个正方形是否存在,如果存在,就删除它的所有木棍,同时res++,然后继续枚举,最后返回res。

// square,stick数组处理技巧
#include<bits/stdc++.h>
using namespace std;
int main(){
	//
	
	 int n;
	 scanf("%d", &n);
     int totstick = (n+1)*n*2;
	 int  totsquare = 0; 
	 int neibor = n*2+1; 

      

        for(int sz = 1; sz <= n; sz++)
        for(int i = 1; (i-1)/neibor+sz <= n; i += neibor)
        for(int j = i; j-i+sz <= n; j++)
        {           
            totsquare ++; 
            int l = j;
             cout<< " sz = " << sz << " i = " << i << " j = " << j << " j-i+sz = " << j-i+sz << " l = " << l << " l-j =  " << l - j;
            for( l ; l-j < sz; l++)
            {
//                square[totsquare].push_back(l);
//                square[totsquare].push_back(l+sz*neibor);
//                stick[l].push_back(totsquare);
//                stick[l+sz*neibor].push_back(totsquare);  
               
                cout<< " totsquare1 = " << totsquare << "  :  " << l <<' '<< l+sz*neibor<<' ';
             
            }
            for(int l = j+n; (l-j-sz)/neibor < sz; l += neibor)
            {
//                square[totsquare].push_back(l);
//                square[totsquare].push_back(l+sz);
//                stick[l].push_back(totsquare);
//                stick[l+sz].push_back(totsquare); 
                  cout<< "   totsquare2 = "<< l <<' '<< l+sz<<' ';
      
            }
            cout<<endl<<endl;
        }

       
    
}

IDA* code

#include<bits/stdc++.h> 
using namespace std;

int T, n, k, totstick, totsquare, neibor, ans, maxd, exi[65], temp[65];

vector <int> stick[65];     // stick[i]记录每条边包含的正方形
vector <int> square[65];    //  square[i]记录每个正方形包含的边

int h()//gu'jiguji
{
    int res = 0;
    for(int i = 1; i <= totsquare; i++) temp[i] = exi[i];
    for(int i = 1; i <= totsquare; i++) if(!temp[i])
    {
        res ++;
        temp[i] -= square[i].size();
        for(int j = 0; j < square[i].size(); j++) 
        for(int l = 0; l < stick[square[i][j]].size(); l++)
        {
            temp[stick[square[i][j]][l]] --;
        }
    }
    return res;
}

bool dfs(int sum)
{
    if(sum+h() >= maxd) return 0;

    int tmp = 1;
    while(exi[tmp] < 0 && tmp <= totsquare) tmp++;  // choose a square (tmp)
    if(tmp > totsquare) {ans = min(sum, ans); return 1;}

    for(int i = 0; i < square[tmp].size(); i++)  // choose which in tmp to destroy
    {
        int sti = square[tmp][i];
        for(int j = 0; j < stick[sti].size(); j++) exi[stick[sti][j]]--;

        if(dfs(sum+1)) return 1;

        for(int j = 0; j < stick[sti].size(); j++) exi[stick[sti][j]]++;
    }
    return 0;
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &k);
        totstick = (n+1)*n*2; totsquare = 0; neibor = n*2+1; 

        for(int i = 1; i < 65; i++)
        {
            stick[i].clear(); square[i].clear();
        }   

        for(int sz = 1; sz <= n; sz++)         //枚举正方形的边长 
        for(int i = 1; (i-1)/neibor+sz <= n; i += neibor)   //枚举行
        for(int j = i; j-i+sz <= n; j++)    //枚举列 
        {
             totsquare ++;
            for(int l = j; l-j < sz; l++)  //枚举正方形的上边 
            {
                square[totsquare].push_back(l);   
                square[totsquare].push_back(l+sz*neibor);  //上边对应的下边 
                stick[l].push_back(totsquare);
                stick[l+sz*neibor].push_back(totsquare);   
            }
            for(int l = j+n; (l-j-sz)/neibor < sz; l += neibor)
            {
                square[totsquare].push_back(l);
                square[totsquare].push_back(l+sz);
                stick[l].push_back(totsquare);
                stick[l+sz].push_back(totsquare);       
            }
        }

        memset(exi, 0, sizeof exi);
        for(int i = 1; i <= k; i++)
        {
            int t; scanf("%d", &t);
            for(int j = 0; j < stick[t].size(); j++)
            {
                exi[stick[t][j]]--;
            }
            totstick --;
        }

        ans = totstick;
        for(maxd = 1; ; maxd++) if(dfs(0)) break;
        printf("%d\n", ans);
    }
    return 0;
}

DLX code:

// 先意思一下,懒得跑了,留着下次再找bug
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;

vector <int> stick[65];     // squares that need stick[i]
vector <int> square[65];    // sticks that square[i] need
int T, n, k, totstick, totsquare, neibor, ans, exi[65], temp[65];

struct DLX{
	int U[maxn], D[maxn], L[maxn], R[maxn], row[maxn], col[maxn], sum[maxn], h[maxn];
	int n, m, time;
	
	void init(int _n, int _m){
		n = _n;
		m = _m;
		
		for(int i = 0; i <= m, i++) {
		  sum[i] = 0;
		  L[i] = i - 1;
		  R[i] = i + 1;
		  U[i] = D[i] = i; 
		  row[i] = 0;
		  col[i] = i;
		} 
		
		for(int i = 1; i <= n; i++)
			h[i] = -1;

		
		L[0] = m;
		R[m] = 0;
		time = m;		
	}
	
	void link(int r, int c){
		row[++time] = r;
		col[time] = c;
		sum[c]++;
		
		U[time] = U[c];
		D[time] = c;
		D[U[c]] = time;
		U[c] = time;
		
		if(h[r] == -1) h[r] = L[time] = R[time] = time;
		else {
			L[time] = L[h[r]];
			R[time] =  h[r];
			R[L[h[r]]] = time;
			L[h[r]] = time;
			
		}
			
	}
	
	bool remove(int c){
		for(int i = D[c]; i != c; i = D[i])
		R[L[i]] = R[i];
		L[R[i]] = L[i];
	}
	
	bool resume(int c){
		for(int i = U[c]; i != c; i = U[i])
		R[L[i]] = L[R[i]] = i;
	}
	
	int f(){
		int res = 0;
		for(int i = 1; i <= totsquare; i++) temp[i] = exi[i];
		for(int i = 1; i <= totsquare; i++)
		if(!temp[i]){
			res++;
			tmp[i]--;
			for(int j = 0; j < square[i].size; j++) 
			for(int k = 0; k < stick[square[i][j]].size; k++) 
			   temp[stick[square[i][j]][k]]--;
				
		}
		
	    return res; 
		
	}
	

	
    
	bool dance(int cnt){
		 if(cnt + f() > ans) return false;
		 
		 if(L[0] == 0){
		 	ans = min(ans, cnt);
			 retrun true; 
		 }
		 int c = R[0];
		 for(int i = R[0]; i != 0; i = R[i]){
		 	if(sum[i] == 0) return false;
		 	if(sum[i] < sum[c]) c = i;
		 }
		 
		 bool flag = 0;
		 for(int i = D[c]; i != c; i = D[i]){
		 	for(int j = R[i]; j != i; j = R[j]) remove(j);
		 	remove(i);
		 	
		    if(dance(cnt + 1) )	flag = 1;
		 	
		 	resume(i);
		 	for(int j = L[i]; j != i; j = L[j]) resume(j);
		 	
		 }
		 return flag;
	    	 
	}
	
	
	clear(){              //清除没有元素的列 
		for(int i = R[0]; i != 0; i = R[0])
			if(sum[i] == 0){
				R[L[i]] = R[i];
			    L[R[i]] = L[i]; 
			}
		
	} 
	
	void solve(){
		
		ios::sycn_with_stdio(false);
		
		
		cin>> T;
		while(T--){
			cin>> n >> k;
			totstick = 2 * n (n + 1), totsquare = 0, neibor = 2 * n + 1,  ans = 65;
			
			for(int sz = 1; sz <= n; sz++)
			for(int i = 1; (i - 1) / n + sz <= n; i += neibor)
			for(int j = i ; j - i + sz <= n; j ++){
				totsquare++;
				
				for(int l = j; l - j < sz; l++){
					square[totsquare].push_back(l);
					square[totsquare].push_back(l + sz * neibor);
					stick[l].push_back(totsquare);
					stick[l + sz * neibor].push_back(totsquare);
				}
				
				for(int l = j + n; (l - j -sz) / neibor < sz; l += neibor){
					square[totsquare].push_back(l);
					square[totsquare].push_back(l + sz);
					stick[l].push_back(totsquare);
					stick[l + sz].push_back(totsquare);
				}
			}
			
			for(int i = 0; i <= 65; i++)
			exi[i] = 0;
			for(int i = 0; i < k; i++){
				int t;
				cin>>t;
				for(int i = 0; i < stick[t].size; i++)
				exi[stick[t][j]]--;
			}
			
			init(totstick, totsquare);  //初始化 
			
			for(int i = 1; i <= totsquare; i++)
			for(int j = 0; j < square[i].szie; j++)
			for(int k = 0; k < stick[square[i][j]].size; k++)
		    if(!exi(stick[square[i][j]][k])) link(square[i][j], stick[square[i][j]][k]);
			
			clear(); //清除没有元素的列,DLX 01 矩阵的行为边,列为正方形,因为输入时有些正方形在开始是已经被破环,要把被破环的正方形从 01 矩阵中清除 
	        
	        dance(0));
			
			cout<<ans<<endl; 
		} 
		
		return ;	 
	}
	
};

int main(){
    DLX dlx;
    dlx.solve();
}

发布了92 篇原创文章 · 获赞 7 · 访问量 3749

猜你喜欢

转载自blog.csdn.net/dajiangyou123456/article/details/104142654
今日推荐