HDU 4979: 更好的DLX模板 + 打表

本題構造矩陣並不難,用所有可能出現的組合當列,用所有可能買到的組合當行,然後跑一個可重複匹配即可……

噁心的事情在於這題數據量有點大,C84 = 70行,可重複覆蓋有點吃不消。

所以應該打表……但是有幾組數據真是需要跑好長時間才能跑出來,感覺是需要手算的,一組是8 3 2, 一組是8 5 4.

判斷是否爲1的話,我們可以用二進制判斷,12345能夠覆蓋1234 => 11111 & 1111 == 1111這樣就可以了。


嗯其實最主要的目的是修一下自己的DLX模板,之前模板爲什麼寫得跟shi一樣……


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
 
const int INF=1<<30;
const int MAXNUM=5000;
 
int u[MAXNUM], d[MAXNUM], l[MAXNUM], r[MAXNUM];//上下左右
int s[MAXNUM], col[MAXNUM], row[MAXNUM];
//s[i]:第i列有幾個節點,即有幾個1,col[i]能告訴你i這個節點在第幾列,row同樣
struct Info // 如有需要,用這個結構體記錄行列表頭的附加信息
{
	int bit_hash;
};
Info column_info[MAXNUM], row_info[MAXNUM];
int row_selected[MAXNUM]; // 是否刪過行
 
int head;//總表頭,其實就是0號節點
int p_nodes;//目前用了多少節點

int column_num=0;//主程序中填寫列數, 行數,也可以動態填寫
int row_num=0;
int ans_num=0;
 
///////////INSERT UNIQUE THINGS HERE
int n, m, k;
int t;
int bit_hash_idx=1;
int two[10];

///////////////////////////////////
 
void del(int c)//注意和精確模板的差別,不用動
{
    for(int i=d[c]; i!=c; i=d[i])
    {
        l[ r[i] ] = l[i];
        r[ l[i] ] = r[i];
        s[ col[i] ] --;
    }
    return;
}
 
void resume(int c)//恢復上面的操作,不用動
{
    for(int i=u[c]; i!=c; i=u[i])
    {
        s[ col[i] ] ++;
        r[ l[i] ] =i;
        l[ r[i] ] = i;
    }  
    return;
}
 
int Hash() // 固定的Astar用函數,只需要修改hs下標 > 列數
{
    int ret=0;
    int hs[100]={0};//給每列用的,所以至少要開到列數那麼大
    for(int c=r[head]; c!=head;c=r[c])
    {
        if(hs[c]==0)
        {
            hs[c]=1;
            ret++;
             
            for(int i=d[c]; i!=c; i=d[i])
            {
                for(int j=r[i]; j!=i; j=r[j])
                {
                    hs[ col[j] ] =1;
                }
            }
        }
    }
    return ret;
}
 
void DFS(int depth, int cost) //基本不用動,看看是否需要記錄答案等就可以了
{
    ////TEST
    //cout<<depth<<endl;
    if(depth-1 + Hash() > row_num)
        return;
    if(cost >= ans_num)
        return;
     
    if(r[head] == head)
    {
        ans_num=min(cost, ans_num);
        return;//矩陣被刪乾淨了
    }
     
    int min1=INF, now;//挑一個1數最少列的先刪
    for(int t=r[head]; t!=head; t=r[t])
    {
        if(s[t]==0)
            return;
        if(s[t] <=min1 )
        {
            min1=s[t];
            now=t;
        }
    }
    //TEST
    //cout<<"now: "<<now<<endl;
     
    int i, j;
    for(i=u[now]; i!=now; i=u[i])
    {
        del(i);
        //↑注意和精確模板的差別
        //枚舉這一列每個1由哪行來貢獻,這行即爲暫時性的答案,
		//如果需記錄都刪了哪些行,此時記錄ans[depth]=row[i]就可以了
		
		int now_ans = row[i];
		
		row_selected[now_ans]++;
		if(row_selected[now_ans]==1)
			cost++;
		
        ///TEST
        //cout<<"ans[depth]: "<<ans[depth]<<endl;
        for(j=r[i]; j!=i; j=r[j])
        {
            del(j);
        }
         
        DFS(depth+1, cost);
         
        for(j=l[i]; j!=i; j=l[j])
        {
            resume(j);
        }
		
		row_selected[now_ans]--;
        if(row_selected[now_ans]==0)
            cost--;
         
        resume(i);
    }
 
    return;
}
 
void init()
{
    memset(u,0,sizeof(u));
    memset(d,0,sizeof(d));
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    memset(s,0,sizeof(s));
    memset(col,0,sizeof(col));
	memset(row,0, sizeof(row));
	memset(column_info, 0, sizeof(column_info));
	memset(row_info, 0, sizeof(row_info));
 
    head=0;
    p_nodes=0;
     
    //INSERT UNIQUE THINGS HERE
	bit_hash_idx = 1;
	ans_num = INF;
	
	return;
}
 
int insert_node(int row_first1, int j, int now_row)//知道我當前的行第一個是誰,我在第j列, 第now_row行,基本不用動
{
    p_nodes++;
    s[j] ++;
    u[p_nodes] = u[j];
    d[ u[j] ] = p_nodes;
    u[j] = p_nodes;
    d[p_nodes] = j;
         
    col[p_nodes] = j;//和列的關係處理完畢
         
    if(row_first1==-1)
    {
        l[p_nodes] = r[p_nodes] = p_nodes;
        row_first1=p_nodes;
    }
    else
    {
        l[p_nodes] = l[row_first1];
        r[ l[row_first1] ] = p_nodes;
        r[p_nodes] = row_first1;
        l[ row_first1 ]=p_nodes;//和行的關係處理完畢
    }
    row[p_nodes]=now_row;
    return row_first1;
     
}
 
void insert_row(int idx)//新建一行,一行裏會插入若干結點,idx = 這是第幾行
{
    int row_first1=-1;//看看這一行是不是已經有第一個1了
     
    int i;
    for(i=1; i<=column_num; i++)
    {
		if( (row_info[idx].bit_hash & column_info[i].bit_hash ) == column_info[i].bit_hash) //是1的條件
			row_first1=insert_node(row_first1, i, idx);
    }
    return;
}
 
void InitLinks()//注意:該鏈表兩個方向都是循環的,最上面的上面指最下面
{
    int i;
    r[head]=1;
    l[head]=column_num;
     
    for(i=1;i<=column_num;i++)//製作列的表頭,使用結點1~column_num
    {
        l[i]=i-1;
        r[i]=i+1;
        if(i==column_num)
            r[i]=head;
        u[i]=i;
        d[i]=i;
        s[i]=0;
        col[i]=i;

		//如有需要,在這裏填寫列表頭的Info,由於已經填寫完了就不填寫了
    }
     
    p_nodes=column_num;
    for(i=1; i<=row_num; i++)
    {
        insert_row(i);//可以在這裏填寫行表頭info
    }
}

void GenerateBit(int now, int now_value, int max_length, Info target[], int pre, int max_idx)//本題特有的填寫行列表頭函數
{
	if(now == max_length+1)
	{
		target[bit_hash_idx].bit_hash = now_value;
		bit_hash_idx++;
		return;
	}
	
	int i;
	for(i=pre+1;i<=n;i++)
	{
		now_value += two[i-1];
		GenerateBit(now+1, now_value, max_length, target, i, max_idx);
		now_value -= two[i-1];
		if(bit_hash_idx > max_idx)
			return;
	}
	return;
}

int C(int x, int y)
{
	int i;
	int up=1, down = 1;
	for(i=1; i<=y; i++)
		up*=x+1-i;
	for(i=y; i>=1; i--)
		down *= i;
	return up/down;
}
 
int main()
{
	//freopen("1.txt", "w", stdout);
	ofstream fout("1.txt");
	two[0]=1;
	for(int tmp=1;tmp<=9;tmp++)
		two[tmp] = 2*two[tmp-1];
	
    scanf("%d", &t);

    for(int files=1; files<=t; files++)
    {
		scanf("%d %d %d", &n, &m, &k);
        column_num=C(n, k);
		row_num = C(n, m);
		GenerateBit(1, 0, k, column_info, 0, column_num);
		bit_hash_idx = 1;
		GenerateBit(1, 0, m, row_info, 0, row_num);
		
        InitLinks();
        DFS(1,0);
		
        printf("Case #%d: ", files);
		cout<<ans_num<<", ";
    }
    system("pause");
    return 0;
}











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章