poj 2441Arrange the Bulls解题报告-状态压缩dp

题目链接:http://poj.org/problem?id=2441

题目描述:有n头牛, m个仓库,每头牛有它喜欢的仓库,每个仓库最多只能安排一头牛,问有多少种安排方法。

 

这道题目应该算是状态压缩dp的简单题目。

如果之前没有接触过状态压缩dp,或者没有很好的理解的状态压缩dp,可以看看这两篇论文:论文1, 论文2。这两篇论文自我感觉都写的很好,尤其是论文2里讲解的那三道题,很不错。

求解:

1>  设计状态:dp[i , j]表示前i头牛形成状态集合j的方法数。

2>  状态转移:dp[i , j] = sum{dp[i-1 , j-(1<<k)]},其中k表示第i头牛可以放在仓库k,则前i-1头牛形成了状态集合j-(1<<k)。

时间复杂度:(n*m*2^m),状态数:(n*2^m)。

这道题可以在空间上优化,类似于01背包的优化,从后向前推,具体见代码。

(ps:一开始没有优化,内存快到50M,是其他人的5-10倍,后来一想可以有滚动数组优化掉,就到了12000K,最后可以类似于01背包空间优化一样,倒着推就可以优化到8000k,自己对这个优化很满意啊,但是咋就这么笨,必须一点点的想起来啊)

代码如下:

/*  
     数组one记录每个状态1的个数,
     数组bull记录每头牛喜欢的仓库,
     bull[i][0]记录第i头牛喜欢仓库的个数,
     仓库的编号是0~n-1。
 */
 
 #include <stdio.h>
 
 const int maxs = 1100000;
 const int maxn = 22;
 
 int dp[maxs], none[maxs];
 int bull[maxn][maxn];
 
 int main () {
 
     int n, m;
 
     scanf("%d%d", &n, &m);
     for (int i = 1, tmp; i <= n; i++) {
         scanf("%d", &tmp);
         bull[i][0] = tmp;
         for (int j = 1; j <= bull[i][0]; j++) {
             scanf("%d", &bull[i][j]);
             bull[i][j]--;            // barn的标号为0~n-1
         }
     }
     int M = 1 << m;
     for (int i = 0; i < M; i++) {
         int num = 0;
         for (int j = 0; j < m; j++) {
             if (i & (1<<j)) {
                 num++;
             }
         }
         none[i] = num;
     }
     for (int i = 1; i <= bull[1][0]; i++) {
         int x = bull[1][i];
         dp[1<<x] = 1;
     }
     for (int i = 2; i <= n; i++) {
         for (int j = M-1; j >= 0; j--) {
             if (none[j] != i) continue;
             for (int k = 1; k <= bull[i][0]; k++) {
                 int x = bull[i][k];
                 if (!(j & (1<<x))) continue;
                 dp[j] += dp[j-(1<<x)];
             }
         }
     }
     int ans = 0;
     for (int i = 0; i < M; i++) {
         if (none[i] == n) {
             ans += dp[i];
         }
     }
     printf("%d\n", ans);
 
     return 0;
 }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值