问题描述
我遇到了这个问题,无法想到一个好的解决方案。问题如下
有N项CS课程和N项EEE课程,都编号为1,2,...,N。
对于每个i,j(1≤i,j≤N),CS路线i和EEE路线j的相容性以整数Aij给出。如果Aij = 1,则CS路线i和EEE路线j不冲突;如果Aij = 0,则会发生冲突。
Ravan正在尝试制作N对,每对包含不冲突的CS路线和EEE路线。在这里,每个CS课程和每个EEE课程都必须完全属于一对。
找到Ravan可以制作N对的模数,取模10 ^ 9 + 7。
Ex.
0 1 1
1 0 1
1 1 1
res : 3
Explanation
Here there are 3 ways to make pairs:
(1,2),(2,1),(3,3)
(1,3),1)
(1,2)
Where (i,j) is pair of CS i course and EEE j course
为解决上述问题,我编写了一个简单的dfs函数,但这似乎是不正确的。而且这是一个非常慢的实现。我的方法如下:
int counter=0;
void dfs(vector<vector<int>>&nums,int n,vector<bool>&visit,int c){
if(c == n-1){
counter++;
return;
}
for(int i=0; i<n; i++){
if(nums[i][c]==0 || visit[i])continue;
visit[i] = true;
dfs(nums,n,visit,c+1);
visit[i] = false;
}
}
void solve(vector<vector<int>>&nums,int n){
vector<bool>visit(n,false);
dfs(nums,0);
cout << counter%1000000007;
}
我在为这个问题的正确方向思考吗?还有什么可以解决这个问题的呢?任何帮助都是有价值的。谢谢!
解决方法
这只是@amit注释的扩展,看起来像是包含-排除。
首先,我们采用多种方法制作N
对。这就是N!
。在您的示例中,该值为6。
all sets of pairs
(1,1),(2,2),(3,3)
(1,3),2)
(1,1)
(1,1)
然后,对于不需要的每对,我们减去制作N
对(包括禁止对)的方法数量。此类对的数量乘以(N-1)!
。在您的示例中,该数字为4。(2对,每对分为两组N
对。)
Contains forbidden pair (1,2)
Contains forbidden pair (2,1)
但是制作N
对(包括2对禁止对)的方式现在已被减去两次。 (也就是说,我们将(1,3)
加一次,减去两次,现在以-1计数。)因此,我们需要重新添加它们。重新添加1:
Contains both forbidden pairs (1,3)
如果存在一组3个禁止对,则我们必须将其重新添加。但是没有。所以我们的最终计算结果是:
6 - (2 + 2) + 1
根据需要给出3。
那么,如何对更大的N
进行此计算呢?我们知道如何计算N!
,(N-1)!
,(N-2)!
等。棘手的是,有多少对禁止配对,2对,3对等等。
为此,我们可以使用动态编程。构建dp
,使dp[k][l]
是l
禁止对(i,j)
的集合数,这些集合可以一起出现在i <= k
的分配中。我们从dp[0] = [1,...]
开始。 dp[N]
会为您提供所有可以分配给每个大小的禁止对对的计数。
在您的示例中,该结构应该结束:
dp = [
[1,0],[1,1,2,]
我们从中获得计算结果
3! * dp[3][0] - 2! * dp[3][1] + 1! * dp[3][2] + 0! * dp[3][3]
= 6 * 1 - 2 * 2 + 1 * 1 - 1 * 0
= 3
那足以让你前进。