📜  位屏蔽和动态编程设置1(为每个人分配唯一上限的方法)

📅  最后修改于: 2021-04-29 13:02:31             🧑  作者: Mango

请考虑以下问题陈述。

有100种不同类型的盖帽,每个盖帽的唯一ID从1到100。此外,有“ n”个人分别具有可变数目的盖帽的集合。有一天,所有这些人都决定参加一个戴着帽子的聚会,但看起来很独特,他们决定他们都不会戴着相同类型的帽子。因此,计算安排或方式的总数,以使他们都不佩戴相同类型的帽子。

约束:1 <= n <= 10示例:

The first line contains the value of n, next n lines contain collections 
of all the n persons.
Input: 
3
5 100 1     // Collection of the first person.
2           // Collection of the second person.
5 100       // Collection of the third person.

Output:
4
Explanation: All valid possible ways are (5, 2, 100),  (100, 2, 5),
            (1, 2, 5) and  (1, 2, 100)

由于方式数量可能很大,因此输出模为1000000007

强烈建议您最小化浏览器,然后自己尝试。
一个简单的解决方案是尝试所有可能的组合。首先从第一个集合中选择第一个元素,将其标记为已访问,然后在其余集合中重复出现。它基本上是一个基于回溯的解决方案。

更好的解决方案是使用Bitmasking和DP 。让我们首先介绍位屏蔽。

什么是位屏蔽?
假设我们有一个从1到N编号的元素集合。如果我们要表示该集合的子集,则可以用N个位的序列对其进行编码(我们通常将此序列称为“掩码”)。在我们选择的子集中,当且仅当设置掩码的第i位(即等于1)时,第i个元素才属于该元素。例如,掩码10000101表示集合[1…8 ]由元素1、3和8组成。我们知道,对于一组N个元素,总共有2 N个子集,因此2 N个掩码是可能的,一个代表每个子集。实际上,每个掩码都是以二进制表示的整数。

我们的主要方法是为每个掩码分配一个值(因此,为每个子集分配一个值),然后使用已经计算出的掩码的值来计算新掩码的值。通常我们的主要目标是计算值/为一套完整的解决方案,即,对于面膜11111111。通常情况下,找到一个子集X的值,我们删除一个元素无微不至和使用值获得的子集X“1,X” 2 …,X“k以计算值/十,为解决这意味着X值”必须已计算的,所以我们需要建立在口罩将被视为排序。很容易看出自然排序会起作用:按相应数字的升序遍历掩码。另外,我们有时,开始与空集X和我们无微不至的添加元素,并使用获得的子集X“1,X” 2 …,X” k以计算值/为X.解决方案的值

我们通常在遮罩上使用以下表示法/操作:
bit(i,mask)–掩码的第i位
count(mask)–掩码中非零位数
first(mask)–掩码中最低的非零位的数目
set(i,mask)–设置掩码中的第i位
check(i,mask)–检查掩码中的第i位

使用位屏蔽+ DP如何解决此问题?
这个想法是利用最多10个人的事实。因此,我们可以使用整数变量作为位掩码来存储哪个人戴着帽子,哪个不戴着帽子。

Let i be the current cap number (caps from 1 to i-1 are already 
processed). Let integer variable mask indicates that the persons w
earing and not wearing caps.  If i'th bit is set in mask, then 
i'th person is wearing a cap, else not.

             // consider the case when ith cap is not included 
                     // in the arrangement
countWays(mask, i) = countWays(mask, i+1) +             
                    
                    // when ith cap is included in the arrangement
                    // so, assign this cap to all possible persons 
                    // one by one and recur for remaining persons.
                    ∑ countWays(mask | (1 << j), i+1)
                       for every person j that can wear cap i 
 
Note that the expression "mask | (1 << j)" sets j'th bit in mask.
And a person can wear cap i if it is there in the person's cap list
provided as input.

如果我们绘制完整的递归树,则可以观察到许多子问题一次又一次地得到解决。因此,我们使用动态编程。使用表dp [] [],以便在每个条目dp [i] [j]中,i是掩码,j是大写数字。

由于我们想访问所有可以戴特定帽子的人,因此我们使用了向量数组capList [101]。 capList [i]值表示可以戴帽i的人员列表。

以下是上述想法的实现。

C/C++
// C++ program to find number of ways to wear hats
#include
#define MOD 1000000007
using namespace std;
  
// capList[i]'th vector contains the list of persons having a cap with id i
// id is between 1 to 100 so we declared an array of 101 vectors as indexing
// starts from 0.
vector capList[101];
  
// dp[2^10][101] .. in dp[i][j], i denotes the mask i.e., it tells that
// how many and which persons are wearing cap. j denotes the first j caps
// used. So, dp[i][j] tells the number ways we assign j caps to mask i
// such that none of them wears the same cap
int dp[1025][101];
  
// This is used for base case, it has all the N bits set
// so, it tells whether all N persons are wearing a cap.
int allmask;
  
// Mask is the set of persons, i is cap-id (OR the 
// number of caps processed starting from first cap).
long long int countWaysUtil(int mask, int i)
{
    // If all persons are wearing a cap so we
    // are done and this is one way so return 1
    if (mask == allmask) return 1;
  
    // If not everyone is wearing a cap and also there are no more
    // caps left to process, so there is no way, thus return 0;
    if (i > 100) return 0;
  
    // If we already have solved this subproblem, return the answer.
    if (dp[mask][i] != -1) return dp[mask][i];
  
    // Ways, when we don't include this cap in our arrangement
    // or solution set.
    long long int ways = countWaysUtil(mask, i+1);
  
    // size is the total number of persons having cap with id i.
    int size = capList[i].size();
  
    // So, assign one by one ith cap to all the possible persons
    // and recur for remaining caps.
    for (int j = 0; j < size; j++)
    {
        // if person capList[i][j] is already wearing a cap so continue as
        // we cannot assign him this cap
        if (mask & (1 << capList[i][j])) continue;
  
        // Else assign him this cap and recur for remaining caps with
        // new updated mask vector
        else ways += countWaysUtil(mask | (1 << capList[i][j]), i+1);
        ways %= MOD;
    }
  
    // Save the result and return it.
    return dp[mask][i] = ways;
}
  
// Reads n lines from standard input for current test case
void countWays(int n)
{
    //----------- READ INPUT --------------------------
    string temp, str;
    int x;
    getline(cin, str);  // to get rid of newline character
    for (int i=0; i> temp)
        {
            stringstream s;
            s << temp;
            s >> x;
  
            // add the ith person in the list of cap if with id x
            capList[x].push_back(i);
        }
    }
    //----------------------------------------------------
  
    // All mask is used to check whether all persons
    // are included or not, set all n bits as 1
    allmask = (1 << n) - 1;
  
    // Initialize all entries in dp as -1
    memset(dp, -1, sizeof dp);
  
    // Call recursive function count ways
    cout << countWaysUtil(0, 1) << endl;
}
  
// Driver Program
int main()
{ 
     int n;   // number of persons in every test case
     cin >> n;
     countWays(n);
     return 0;
}


Java
// Java program to find number of ways to wear hats
  
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Vector;
  
class Test
{
    static final int MOD = 1000000007;
      
    // for input
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      
    // capList[i]'th vector contains the list of persons having a cap with id i
    // id is between 1 to 100 so we declared an array of 101 vectors as indexing
    // starts from 0.
    static Vector capList[] = new Vector[101];
      
       
    // dp[2^10][101] .. in dp[i][j], i denotes the mask i.e., it tells that
    // how many and which persons are wearing cap. j denotes the first j caps
    // used. So, dp[i][j] tells the number ways we assign j caps to mask i
    // such that none of them wears the same cap
    static int dp[][] = new int[1025][101];
       
    // This is used for base case, it has all the N bits set
    // so, it tells whether all N persons are wearing a cap.
    static int allmask;
       
    // Mask is the set of persons, i is cap-id (OR the 
    // number of caps processed starting from first cap).
    static long countWaysUtil(int mask, int i)
    {
        // If all persons are wearing a cap so we
        // are done and this is one way so return 1
        if (mask == allmask) return 1;
       
        // If not everyone is wearing a cap and also there are no more
        // caps left to process, so there is no way, thus return 0;
        if (i > 100) return 0;
       
        // If we already have solved this subproblem, return the answer.
        if (dp[mask][i] != -1) return dp[mask][i];
       
        // Ways, when we don't include this cap in our arrangement
        // or solution set.
        long ways = countWaysUtil(mask, i+1);
       
        // size is the total number of persons having cap with id i.
        int size = capList[i].size();
       
        // So, assign one by one ith cap to all the possible persons
        // and recur for remaining caps.
        for (int j = 0; j < size; j++)
        {
            // if person capList[i][j] is already wearing a cap so continue as
            // we cannot assign him this cap
            if ((mask & (1 << capList[i].get(j))) != 0) continue;
       
            // Else assign him this cap and recur for remaining caps with
            // new updated mask vector
            else ways += countWaysUtil(mask | (1 << capList[i].get(j)), i+1);
            ways %= MOD;
        }
       
        // Save the result and return it.
        return dp[mask][i] = (int) ways;
    }
       
    // Reads n lines from standard input for current test case
    static void countWays(int n) throws Exception
    {
        //----------- READ INPUT --------------------------
        String str;
        String split[];
        int x;
                
        for (int i=0; i();
          
          
        n = Integer.parseInt(br.readLine());
        countWays(n);
    }
}
// This code is contributed by Gaurav Miglani


Python
#Python program to find number of ways to wear hats
from collections import defaultdict
  
class AssignCap:
  
    # Initialize variables
    def __init__(self):
  
            self.allmask = 0
  
            self.total_caps = 100
  
            self.caps = defaultdict(list)
  
  
    #  Mask is the set of persons, i is the current cap number.
    def countWaysUtil(self,dp, mask, cap_no):
          
        # If all persons are wearing a cap so we
        # are done and this is one way so return 1
        if mask == self.allmask:
            return 1
  
        # If not everyone is wearing a cap and also there are no more
        # caps left to process, so there is no way, thus return 0;
        if cap_no > self.total_caps:
            return 0
  
        # If we have already solved this subproblem, return the answer.
        if dp[mask][cap_no]!= -1 :
            return dp[mask][cap_no]
  
        # Ways, when we don't include this cap in our arrangement
        # or solution set
        ways = self.countWaysUtil(dp, mask, cap_no + 1)
          
        # assign ith cap one by one  to all the possible persons
        # and recur for remaining caps.
        if cap_no in self.caps:
  
            for ppl in self.caps[cap_no]:
                  
                # if person 'ppl' is already wearing a cap then continue
                if mask & (1 << ppl) : continue
                  
                # Else assign him this cap and recur for remaining caps with
                # new updated mask vector
                ways += self.countWaysUtil(dp, mask | (1 << ppl), cap_no + 1) 
  
                ways = ways % (10**9 + 7)
  
        # Save the result and return it
        dp[mask][cap_no] = ways
  
        return dp[mask][cap_no]
  
  
  
    def countWays(self,N):
  
        # Reads n lines from standard input for current test case
        # create dictionary for cap. cap[i] = list of person having
        # cap no i
        for ppl in range(N):
  
            cap_possessed_by_person = map(int, raw_input().strip().split())
  
            for i in cap_possessed_by_person:
  
                self.caps[i].append(ppl)
  
        # allmask is used to check if all persons
        # are included or not, set all n bits as 1
        self.allmask = (1 << N) -1
  
        # Initialize all entries in dp as -1
        dp = [[-1 for j in range(self.total_caps + 1)] for i in range(2 ** N)]
  
        # Call recursive function countWaysUtil
        # result will be in dp[0][1]
        print self.countWaysUtil(dp, 0, 1,)
  
#Driver Program
def main():
    No_of_people = input() # number of persons in every test case
  
    AssignCap().countWays(No_of_people)
  
  
if __name__ == '__main__':
    main()
  
# This code is contributed by Neelam Yadav


输入:

3               
5 100 1         
2               
5 100

输出:

4