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


    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
    ",sum);
            }
            return 0;
        }

    4.总结

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

  • 相关阅读:
    最长递增子序列问题---动态规划
    Shell中判断语句if中-z至-d的意思
    Linux中shell变量$0,$?等含义
    LeetCode之链表
    linux命令之crontab定时执行任务
    linux命令之scp远程文件复制
    Linux命令之sed批量替换字符串操作
    tomcat安装出现问题及解决方法
    LeetCode之二叉树作题java
    mysql导入导出、阿里云内网传输
  • 原文地址:https://www.cnblogs.com/khbcsu/p/4245943.html
Copyright © 2020-2023  润新知