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;
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值