【校内互测】笨笨的雕塑安置(容斥原理)

【描述】

Wcyz为了迎接百年校庆,美化校园,请了校友笨笨将n座雕塑,准备安置在校园内,整个校园可以抽象成一个nxn的大网格,每个lxl网格最多只能安置一座雕塑,但是某些lxl的网格上恰好是一个食堂或湖泊,这些网格是不能安置雕塑的,每个雕塑的造型相同,这样同一种安置方案中交换排列都算一种。任意雕塑在同一行或同一列是不合法的方案。
    学校想知道有多少种安置方案,笨笨想从中选择最好的一种方案,笨笨想请你告诉他方案种数。
    【输入格式】
    第一行,两个整数n,m(n≤20,m≤10),用空格隔开,n表示nXn的大网格,m表示不能安置雕塑的位置的个数。
    第二行至m+l行,每行两个数x,y,用空格分开,表示坐标(x,y)的lxl的网格上不能安置雕塑。
    【输出格式】
    一个数,方案种数(方案种数≤263_1)
    Sample input
    6 7
    11
    21
    2 2
    3 3
    3 4
    4 3
    4 4
Sample output
184
【代码】

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,q,r[50],a[50][5];
bool h[50],s[50];
long long d[50],ans;
inline void dfs(int o,int t){
	if (!t) {
		r[q]++; return;
	}
	for (int i=o+1;i<=m;++i)
	  if (!h[a[i][1]]&&!s[a[i][2]]){
	  	h[a[i][1]]=true; s[a[i][2]]=true;
	  	dfs(i,t-1);
	  	h[a[i][1]]=false; s[a[i][2]]=false;
	  }
}
int main(){
	freopen("sculpture.in","r",stdin);
	freopen("sculpture.out","w",stdout); 
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;++i) scanf("%d%d",&a[i][1],&a[i][2]);
	for (q=1;q<=m;++q) dfs(0,q);//搜索实现的目标是:r[i]表示把i个雕塑放到不能放的格子里的方案数 
	d[1]=1; d[0]=1;
	for (int i=2;i<=n;++i) d[i]=d[i-1]*i;//d[i]表示的是i的阶乘的数值 
	ans=1;
	for (int i=2;i<=n;++i) ans*=i;//ans先赋成n的阶乘,这是没有一个点不能放的最好情况 
	for (int i=1;i<=m;++i)//这里好像就是传说中的容斥原理;首先减去有一个雕塑放在不能放的地方的情况(剩下的情况数是d 
	  if (i%2==1) ans-=r[i]*d[n-i];//[n-i]),这样的话发现把有两个雕塑、三个雕塑。。。的都减去了,所以以后还要加回来。 
	  else ans+=r[i]*d[n-i];//以此类推 
	cout<<ans;
}


你可能感兴趣的:(搜索,容斥原理)