UVA11806【拉拉队】Cheerleaders-------2015年1月24日

本文探讨了在一个M行N列的广场上安排K个拉拉队员的问题,并提出了使用容斥原理解决该问题的方法。文章详细介绍了算法思路,包括如何通过预处理组合数避免重复计算,以及如何利用二进制状态表示进行容斥计算。

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.总结

本题要注意正难则反策略,容斥原理,以及代码实现容斥原理的应用。

转载于:https://www.cnblogs.com/khbcsu/p/4245943.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值