标签:
1.题意描述
本题大致意思是讲:给定一个广场,把它分为M行N列的正方形小框。现在给定有K个拉拉队员,每一个拉拉队员需要站在小框内进行表演。但是表演过程中有如下要求:
(1)每一个小框只能站立一个拉拉队员;
(2)广场的第一行,最后一行,第一列,最后一列都至少站有一个拉拉队员;
(3)站在广场的四个角落的拉拉队员可以认为是同时占据了一行和一列。
2.思路分析:
本题如果直接枚举的话难度很大并且会无从下手。那么我们是否可以采取逆向思考的方法来解决问题呢?我们可以用总的情况把不符合要求的减掉就行了。
首先我们如果不考虑任何约束条件,我们可以得出如下结论:
下载我们假定第一行不站拉拉队员的所有的站立方法有A种。最后一行不站拉拉队员的所有的方法有B种。第一列不站拉拉队员的所有的站立方法有C种。最后一列不站拉拉队员的站立方法有D种。
下面我们可以得出最后结果:
下面问题来了我们如何利用代码实现容斥原理呢?我们可以借用离散数学的最大项和最小项知识结合与运算来判断每一项的特征。比如说,含A的和1进行与运算。含B的与2进行与运算。含C的和4进行与运算。含D的和8进行与运算。
然后对于每一种状态,我们利用数字0-15来代替。
在进行这些工作之前,我们还要进行基础性工作,数据初始化和打表。
对于如何打表,我们可以采取组合数公式的递推式进行。打表过程中一定要注意边界问题的处理,要不极容易出错。
3.AC代码
#include<iostream> #include<cstdio> #include<algorithm>//注意头文件的使用 #include<string.h> using namespace std; #define maxn 500+5 const int mod=1000007; int c[maxn][maxn];//数组c[i][j]表示在i个可用的点中抽取j个点的情况总数 void process()//函数预处理过程相当于打表这样节省时间 { memset(c,0,sizeof(c)); c[0][0]=1; for(int i=1;i<maxn;i++) { c[i][0]=c[i][i]=1;//初始化边界 for(int j=1;j<i;j++)//注意是j<i不能超多这是由于C[i][j]的定义得来的 c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;//组合数公式的递推式 } } int main() { process(); int T; cin>>T; for(int Case=1;Case<=T;Case++) { int sum=0; int n,m,k; cin>>n>>m>>k; for(int s=0;s<16;s++)//处理容斥原理的方式,与运算 { //当s=0时就相当于我们在分析过程中的sum1 int r=n,c1=m;int b=0;//注意理解其中的b if(s&1) {r--;b++;} if(s&2){r--;b++;} if(s&4){c1--;b++;} if(s&8){c1--;b++;} if(b&1){sum=(sum+mod-c[r*c1][k])%mod;}//说明含有奇数个项如ABD,A等 else sum=(sum+c[r*c1][k])%mod;//说明含有偶数个项如ABCD,AB等 } printf("Case %d: ",Case); printf("%d\n",sum); } return 0; }
4.总结
本题要注意正难则反策略,容斥原理,以及代码实现容斥原理的应用。
UVA11806【拉拉队】Cheerleaders-------2015年1月24日
标签:
原文地址:http://www.cnblogs.com/khbcsu/p/4245943.html