hdu4979 A simple math problem.Dancing Links,打表

来源:互联网 发布:mmd虎视眈眈镜头数据 编辑:程序博客网 时间:2024/05/21 17:50
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分钟。


#include<iostream>#include<cstdio>#include<cstring>using namespace std;#define NN 75#define MM 75#define INF 101000int 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;}


0 0
原创粉丝点击