题目大意:
题目链接:https://jzoj.net/senior/#main/show/3059
求含个障碍网格中选择个格子,使得任意两个格子不在同一行、同一列的方案数。
思路:
由于只有,可以考虑用总方案数放在障碍上的方案数。
总方案数很明显是
因为第一行有个格子可以选,选择其中一个后,第二行就有个格子可选,第三行就有个可选,一直到最后一行只有个可选。总方案数就是
然后我们会发现总方案数里面包含了选择个障碍的方案数。所以我们再减去选择个的方案数。此时我们会发现,总方案数里面包含了选择个方案数,减去选择一个的方案数后,减去的也包含了选择个障碍的方案数。相抵消,就等于没有减去选择个障碍的方案数。于是我们就要减去选择个的方案数。然后加上选择个,减去选择个。。。
用人话说,设表示在网格中选择个格子,其中有个格子是障碍的情况数量。那么答案就是
那么如何求
很明显,选择个障碍的方案数选择个非障碍的方案数。
选择个障碍的方案数可以用搜索求。因为只有,可以判断是否选择则个障碍。
那么选择个非障碍的方案数其实就是要我们求在的格子里选择个格子的方案数,自然就是了。
的阶乘要提前预处理,时间复杂度。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
int n,m,x[15],y[15];
ll ans,num[25],s;
bool used[3][25];
void dfs(int i,int cnt,int sum)
//i表示处理到第i个障碍,cnt表示选择了多少个障碍,sum表示需要选择的总障碍数
{
if (cnt==sum) //选择完毕
{
s++; //方案数
return;
}
if (i>m) return;
if (!used[1][x[i]]&&!used[2][y[i]]) //这一行和这一列都没有被选择
{
used[1][x[i]]=used[2][y[i]]=1;
dfs(i+1,cnt+1,sum);
used[1][x[i]]=used[2][y[i]]=0;
}
dfs(i+1,cnt,sum);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&x[i],&y[i]);
num[0]=1;
for (int i=1;i<=n;i++) //预处理阶乘
num[i]=i*num[i-1];
ans=num[n];
for (int i=1;i<=m;i++)
{
s=0;
dfs(1,0,i);
if (!s) break;
if (i&1) ans-=s*num[n-i];
else ans+=s*num[n-i];
}
printf("%lld
",ans);
return 0;
}