您的位置:首页 > 其它

hdu4979 A simple math problem.Dancing Links,打表

2014-08-23 15:58 288 查看
hdu4979 A simple math problem.

题意是,给你n、m、r,问1..n中选m个数的集合至少要选多少个,才能包含所有1..n中选r个数的集合。(不重复)

题解说这个题目没有多项式解法。只能用搜索。然后建议了Dancing links。

然后标程就给了一个表有木有!

这种题你也敢出出来,逗我呢啊。

然后我还是试了下DLX的解法。

先回顾一下DLX:

首先我们构造一个01矩阵g
[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=last;U=D=td;C=i;S=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
[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: