`
linest
  • 浏览: 150291 次
  • 性别: Icon_minigender_1
  • 来自: 内蒙古
社区版块
存档分类
最新评论

ZOJ-3305* 二进制子集划分

    博客分类:
  • acm
 
阅读更多
3305:有N中调料,M种配方。调料每种只能用一次,求能实现多少种配方。
即统计能划分出多少备选子集。


思路:本题代码参考自http://akheyun.blog.163.com/blog/static/13824927620102183300293/
核心思想是二进制表现。
x = (x - 1) & st 实现了子集遍历
比如 st=1110
1101 & 1110 = 1100
1011 & 1110 = 1010
1001 & 1110 = 1000
0111 & 1110 = 0110
0101 & 1110 = 0100
0011 & 1110 = 0010
0001 & 1110 = 0000

联想起求一个数的二进制中1有多少。
用到 x = (x-1) & x  这每次会使一个1变为0 用时可以小于O(n)

#include<iostream>
using namespace std;
#include<stdio.h>
#include<memory.h>

const int M = 1 << 16 + 1;
bool a[M];
int dp[M];

int solve(int st)
{
         int i;
         if (dp[st] != -1) return dp[st]; //已经计算过,直接返回
         dp[st] = 0;
         for (int x = st; x != 0; x = (x - 1) & st)
         {
               //不使用x能达到的配方数和使用x后配方数的最大值
               if (a[x])
                   dp[st] = max(dp[st], solve(st - x) + 1);
         }
         return dp[st];
}

 

int main ()
{

         int i, j, n, m, k, p;
         while (scanf("%d%d", &n, &m) != EOF)
         {
               memset(a, false, sizeof(a));
               memset(dp, -1, sizeof(dp));

               for (i = 0; i < m; i++)
               {
                    scanf("%d", &k);
                    j = 0;
                    while (k--)
                    {
                          scanf("%d", &p);
                          j += (1 << (p - 1)); //每种配方用2进制表示
                    }
                    a[j] = 1;
               }            

			   //初始全为1传入
               printf("%d\n", solve((1 << n) - 1));
         }

}


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics