• poj 2441 Arrange the Bulls


    状态压缩DP

    多数也把这题分类在图论中,算是状态压缩在图论中的一个应用

    题意:有n只牛和m个场,下面n行给出每只牛喜欢去的场的个数,再给出每个场的编号(而且每只牛只能去他们喜欢的场)。然后要你安排好这些牛去他们喜欢的场,一个场只能有一只牛,问有多少种分配方案

    状态压缩,定义一个m位长的二进制数,从右到左依次代表第1,第2,第3个场,1表示这个场已经被占用,0表示没有。最后我们是要把n个牛都安排进去,那么这个二进制数将有n个1,这些就是我们要的目标状态。显然我们是按照牛的个数进行DP,先放第1只牛,再放第2只……最后放第n只。

    所以状态转移可以表示为  s'--->s , 其中s‘有k-1个1,s有k个1,表示从第k-1个牛到第k个牛的转移,另外看消掉的1在哪一位也就是那个农场,必须满足第k只牛是喜欢这个农场的

    边界条件dp[0]=1;  即一个1都没有的状态

    记忆化搜索,写起来方便一点

    /*
    m个农场,用一个长度为m的二进制表示,第i个农场被占据了则为1,否则为0
    目标状态是有n个农场被占据,对应过来就是一个十进制数转为二进制有n个1
    我们以牛的编号进行dp,从第一只牛开始放,一直放到第n只牛
    所以每次状态转移,我们考虑放进第i个牛有多少方案,把所有可能的加起来即可
    这种类型的状态压缩,用记忆化搜索实现比较容易,用递推则要做多点工作
    为了锻炼代码能力和加深递推的理解,决定两种都写
    */
    #include <cstdio>
    #include <cstring>
    #define N 25
    #define M 25
    #define MAX 1100000
    
    long long dp[MAX];
    int n,m;
    bool g[N][M]; //g[i][j]=1表示第i只牛可以第j个农场
    
    int bit(long long num)
    {//计算十进制数num对应的二进制数有多少个1
        int count=0;
        while(num)
        {
            count += num&1;
            num=num>>1;
        }
        return count;
    }
    
    long long dfs(long long s , int numb)
    {
        if(dp[s]!=-1)
            return dp[s];
        if(!s || !numb) //全部为0
            return dp[s]=1;
        dp[s]=0;
        for(int i=0; i<m; i++)
        {
            if(g[numb][i+1] && s&(1<<i))
            {//s的二进制的第i位有1即该农场有牛,且numb牛可以去那个农场
                dp[s] += dfs(s-(1<<i),numb-1);
            }
        }
        return dp[s];
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            memset(g,false,sizeof(g));
            int num,tmp;
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&num);
                for(int j=1; j<=num; j++)
                {
                    scanf("%d",&tmp);
                    g[i][tmp]=1;
                }
            }
    
            long long ans=0;
            int numb;  //记录一个状态有多少个1
            memset(dp,-1,sizeof(dp));
            for(long long i=0; i<(1<<m); i++)
            {
                numb=bit(i);
                if(numb==n) //是我们要找的目标状态
                    ans += dfs(i,numb);
            }
            printf("%lld\n",ans);
        }
        return 0;
    }

    递推,先预处理一下。init()函数就是给所有可能的状态分类,1的个数相同点的状态把他们放到一起,不用每次都计算,但是这样做似乎并没有提高时间,反而比记忆化搜索更慢了

    #include <cstdio>
    #include <cstring>
    #define MAX 1100000
    #define MAXS 1100000
    #define N 25
    #define M 25
    
    bool g[N][M];
    int c[N];
    int state[N][MAXS];
    long long dp[MAX];
    
    int bit(int s)
    {
        int count=0;
        while(s)
        {
            count += s&1;
            s=s>>1;
        }
        return count;
    }
    
    void init()
    {//最多的状态为2^20-1
        memset(c,0,sizeof(c));
        for(int i=0; i<(1<<20); i++)
        {
            int numb=bit(i);
            state[numb][c[numb]++]=i;
        }
    }
    
    
    int main()
    {
        init();
        int n,m,maxs;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
          maxs=(1<<m)-1;
          memset(g,0,sizeof(g));
          for(int i=0; i<n; i++)
          {
            int cc,k;
            scanf("%d",&cc);
            for(int j=0; j<cc; j++)
            {
              scanf("%d",&k);
              g[i+1][k]=true;
            }
          }
          /**********************************/
          memset(dp,0,sizeof(dp));
          dp[0]=1;
          for(int nn=1; nn<=n; nn++) //枚举所有的牛怎么放
          {
            for(int i=0; i<c[nn]; i++) //枚举有nn个1的状态
            {
              int s=state[nn][i];
              if(s>maxs) continue;
              for(int k=0; k<m; k++)
                if((s&(1<<k)) && g[nn][k+1])
                  dp[s] += dp[s-(1<<k)];
            }
          }
    
          long long ans=0;
          for(int i=0; i<c[n]; i++)
          {
            int s=state[n][i];
            if(s>maxs) continue;
            ans += dp[s];
          }
    
          printf("%lld\n",ans);
        }
        return 0;
    }
  • 相关阅读:
    js的浅拷贝与深拷贝
    用Nodejs连接MySQL(原文链接)
    HTML5交互性图表库
    GitHub Desktop离线安装包
    docker--Dockerfile--sonarqube
    docker --Nexus仓库
    docker --Dockerfile--一些语法
    zookeeper 四字命令
    docker --swarm创建一个集群
    docker --swarm启动2375端口监听
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2958136.html
Copyright © 2020-2023  润新知