hdu4979 A simple math problem.
题意是,给你n、m、r,问1..n中选m个数的集合至少要选多少个,才能包含所有1..n中选r个数的集合。(不重复)
题解说这个题目没有多项式解法。只能用搜索。然后建议了Dancing links。
然后标程就给了一个表有木有!
这种题你也敢出出来,逗我呢啊。
然后我还是试了下DLX的解法。
先回顾一下DLX:
首先我们构造一个01矩阵g[n][m]。如果说g[i][j]==1,就叫i行覆盖了j列。
DLX的两种经典问题:
1.精确覆盖问题:取某些行,这些行覆盖所有的列,且每个列都有且只有一个1。
2.重复覆盖问题:取某些行,这些行覆盖所有的列。一般来说这样的问题要求行数的最小值。
对于本题:
先建立所有的取m和取r的集合,如果某个取m集合能包含取r的集合,那么就在关系矩阵上的该位置为1.
例如,n=2,m=1,r=1.取m个的集合有两种{1}、{2},取r个集合也是两种{1}、{2}。
那么关系矩阵g就是
1 0
0 1
每行代表某个取m的集合能包含哪些某个取r的集合。
那么本题的目标就是求最小的行数量,使得所有列都被至少一行覆盖。这是一个重复覆盖问题。
然后枚举所有的n,m,r,因为他们都很小,所以打表就可以愉快地AC了。
我的程序在我的电脑上运行了5分钟。
题意是,给你n、m、r,问1..n中选m个数的集合至少要选多少个,才能包含所有1..n中选r个数的集合。(不重复)
题解说这个题目没有多项式解法。只能用搜索。然后建议了Dancing links。
然后标程就给了一个表有木有!
这种题你也敢出出来,逗我呢啊。
然后我还是试了下DLX的解法。
先回顾一下DLX:
首先我们构造一个01矩阵g[n][m]。如果说g[i][j]==1,就叫i行覆盖了j列。
DLX的两种经典问题:
1.精确覆盖问题:取某些行,这些行覆盖所有的列,且每个列都有且只有一个1。
2.重复覆盖问题:取某些行,这些行覆盖所有的列。一般来说这样的问题要求行数的最小值。
对于本题:
先建立所有的取m和取r的集合,如果某个取m集合能包含取r的集合,那么就在关系矩阵上的该位置为1.
例如,n=2,m=1,r=1.取m个的集合有两种{1}、{2},取r个集合也是两种{1}、{2}。
那么关系矩阵g就是
1 0
0 1
每行代表某个取m的集合能包含哪些某个取r的集合。
那么本题的目标就是求最小的行数量,使得所有列都被至少一行覆盖。这是一个重复覆盖问题。
然后枚举所有的n,m,r,因为他们都很小,所以打表就可以愉快地AC了。
我的程序在我的电脑上运行了5分钟。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define NN 75
#define MM 75
#define INF 101000
int g[NN][MM],tg[NN][MM];
int R[NN*MM],L[NN*MM],U[NN*MM],D[NN*MM],C[NN*MM],S[MM],cnt[MM];
int head,td;
int ans[10][10][10];
void remove(int c){//仅删除列
for(int i=D[c];i!=c;i=D[i]){
R[L[i]]=R[i];L[R[i]]=L[i];
}
}
void resume(int c){//恢复列
for(int i=U[c];i!=c;i=U[i]){
R[L[i]]=i;L[R[i]]=i;
}
}
bool has[NN];
int hash(){
int ret=0,i,j,c,last;
memset(has,0,sizeof(has));
for(c=R[head];c!=head;c=R[c]){
if (!has[c]){
has[c]=1;
ret++;
for(i=D[c];i!=c;i=D[i])
for (j=R[i];j!=i;j=R[j]){
//printf("hasc=%d %d %d\n",j,i,c);
has[C[j]]=1;
}
}
}
//printf("has=%d\n",ret);
return ret;
}
void dfs(int nans,int &ans){
if (nans+hash()>=ans) return;
if (R[head]==head) {if (nans<ans) ans=nans; return;}//找到一组解,列头链表为空
//对于不必消去所有列的题目,不必消去的列放在后面,R[head]>xx||R[head]==head则找到解
int i,j=INF,c;
for(i=R[head];i!=head;i=R[i]){//找总数最小的一个列,不必消去所有列的题目i<=xx
if (S[i]<j) {j=S[i];c=i;}
}
if (j==0) return;//无法找到覆盖行,无解返回
for(i=D[c];i!=c;i=D[i]){ //枚举用哪行覆盖该列
remove(i);
for(j=R[i];j!=i;j=R[j]) remove(j);//删除该行为1的列
dfs(nans+1,ans);
for(j=L[i];j!=i;j=L[j]) resume(j);//回溯恢复
resume(i);
}
}
int DLX(int n,int m){
head=td=0;
int last=head,i,j,tt,fi;
for(i=1;i<=m;++i){
cnt[i]=0;
for(j=1;j<=n;++j)if (g[j][i]) cnt[i]++;
}
for(i=1;i<=m;i++){//建立列头链表
R[last]=++td;L[td]=last;U[td]=D[td]=td;C[td]=i;S[td]=cnt[i];last=td;
}
R[last]=head;L[head]=last;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if (g[i][j]) tg[i][j]=++td;
else tg[i][j]=0;
//结点映射
/*
for(i=1;i<=n;++i){
for(j=1;j<=m;++j){
printf("%d %d ",g[i][j],tg[i][j]);
}
printf("\n");
}
*/
//printf("td=%d\n",td);
for(j=1;j<=m;j++){//建立上下链表
last=j;
for(i=1;i<=n;i++)if (tg[i][j]){
tt=tg[i][j];
D[last]=tt;U[tt]=last;C[tt]=j;//建立上下链表时维护D,U,C
last=tt;
}
D[last]=j;U[j]=last;
}
for(i=1;i<=n;i++){//左右链表
for(j=1;j<=m;j++)if (tg[i][j]){
last=fi=tg[i][j];
for(;j<=m;j++)if (tg[i][j]){
tt=tg[i][j];
R[last]=tt;L[tt]=last;//L,R
//printf("cacaca %d %d %d\n",tt,R[last],L[tt]);
last=tt;
}
R[last]=fi;L[fi]=last;
}
}
int ans=m;//本题最差结果是列的数量
dfs(0,ans);
return ans;
}
int ex[10];
int st1[NN][10],st2[NN][10],sv[10];
bool contain(int a,int m,int b,int r){
int i;
for(i=1;i<=8;++i) ex[i]=0;
for(i=1;i<=m;++i) ex[st1[a][i]]=1;
for(i=1;i<=r;++i) if (ex[st2[b][i]]==0) return false;
return true;
}
void getnum(int st[NN][10],int now,int m,int n,int step,int &totn){
int i;
if (now!=m&&step>n) return;
if (now==m){
totn++;
for(i=1;i<=m;++i){
st[totn][i]=sv[i];
}
return;
}
for(i=step;i<=n;++i){
sv[now+1]=i;
getnum(st,now+1,m,n,i+1,totn);
}
}
int makeset(int st[NN][10],int n,int m){
int totn=0;
getnum(st,0,m,n,1,totn);
return totn;
}
void make_graph(int n,int m,int r){
int i,j;
int sn1=makeset(st1,n,m);
int sn2=makeset(st2,n,r);
//printf(" %d %d %d %d %d\n",n,m,r,sn1,sn2);
for(i=1;i<=sn1;++i){
for(j=1;j<=sn2;++j){
if (contain(i,m,j,r)) g[i][j]=1;
else g[i][j]=0;
}
}
ans[n][m][r]=DLX(sn1,sn2);
}
void output(){
int i,j,k;
printf("{\n");
for(i=1;i<=8;++i){
printf(" {\n");
for(j=1;j<=8;++j){
printf(" {");
for(k=1;k<=8;++k){
printf(" % 2d",ans[i][j][k]);
if (k!=8) printf(",");
}
printf("}");
if (j!=8) printf(",");
printf("\n");
}
printf(" }");
if (i!=8) printf(",");
printf("\n");
}
printf("};\n");
}
int ans2[10][10][10]=
{
{
{ 1, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 2, 0, 0, 0, 0, 0, 0, 0},
{ 1, 1, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 3, 0, 0, 0, 0, 0, 0, 0},
{ 2, 3, 0, 0, 0, 0, 0, 0},
{ 1, 1, 1, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 4, 0, 0, 0, 0, 0, 0, 0},
{ 2, 6, 0, 0, 0, 0, 0, 0},
{ 2, 3, 4, 0, 0, 0, 0, 0},
{ 1, 1, 1, 1, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 5, 0, 0, 0, 0, 0, 0, 0},
{ 3, 10, 0, 0, 0, 0, 0, 0},
{ 2, 4, 10, 0, 0, 0, 0, 0},
{ 2, 3, 4, 5, 0, 0, 0, 0},
{ 1, 1, 1, 1, 1, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 6, 0, 0, 0, 0, 0, 0, 0},
{ 3, 15, 0, 0, 0, 0, 0, 0},
{ 2, 6, 20, 0, 0, 0, 0, 0},
{ 2, 3, 6, 15, 0, 0, 0, 0},
{ 2, 3, 4, 5, 6, 0, 0, 0},
{ 1, 1, 1, 1, 1, 1, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 7, 0, 0, 0, 0, 0, 0, 0},
{ 4, 21, 0, 0, 0, 0, 0, 0},
{ 3, 7, 35, 0, 0, 0, 0, 0},
{ 2, 5, 12, 35, 0, 0, 0, 0},
{ 2, 3, 5, 9, 21, 0, 0, 0},
{ 2, 3, 4, 5, 6, 7, 0, 0},
{ 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{ 8, 0, 0, 0, 0, 0, 0, 0},
{ 4, 28, 0, 0, 0, 0, 0, 0},
{ 3, 11, 56, 0, 0, 0, 0, 0},
{ 2, 6, 14, 70, 0, 0, 0, 0},
{ 2, 4, 8, 20, 56, 0, 0, 0},
{ 2, 3, 4, 7, 12, 28, 0, 0},
{ 2, 3, 4, 5, 6, 7, 8, 0},
{ 1, 1, 1, 1, 1, 1, 1, 1}
}
};
int main(){
/*
freopen("4979out.txt","w",stdout);
int i,j,k;
memset(ans,0,sizeof(ans));
for(i=1;i<=8;++i){
for(j=1;j<=i;++j){
for(k=1;k<=j;++k){
make_graph(i,j,k);
printf("%d %d %d %d ",i,j,k,ans[i][j][k]);
}
printf("\n");
}
printf("\n");
}
output();
*/
int t,cas,n,m,r;
scanf("%d",&t);
cas=0;
while(t--){
scanf("%d%d%d",&n,&m,&r);
printf("Case #%d: %d\n",++cas,ans2[n-1][m-1][r-1]);
}
return 0;
}