请考虑以下问题陈述。
有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