本题构造矩阵并不难,用所有可能出现的组合当列,用所有可能买到的组合当行,然后跑一个可重复匹配即可……
恶心的事情在于这题数据量有点大,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;
}