CheerLeaders Uva 11806(容斥原理)

题意

给定一个n * m矩阵,和 k 个石子,将 k 个石子全都放到这个矩阵中,使得第一行、第一列、最后一行、最后一列都必须要有石子,请问有多少种放法?

分析

问题的正面不容易解,我们就从问题的反面开始研究,我们将k个石子没有限制的放到矩阵中的方法定义为全集S,我们所想要求解的集合T也一定在这个全集中,我们分析后发现,如果我们设第一行没有石子的情况为集合A,第一列没有石子为集合B,最后一行没有石子为集合C,最后一列没有石子为集合D,那么我们的 T=S|ABCD| 根据容斥定理有 |ABCD|=|A|+|B|+|C|+|D||AB||AC||AD||BC||BD||BD|... 计算集合A、B、C、D的值都不是难事,所以我们可以很方便的求解问题啦。
另外在编码中我们常用状态压缩和为运算来完成集合的交并运算。我们用值s的二进制位的每一位来表示一个集合是否参与交,之后统计参与交的集合个数就可以知道这个状态是应该剪去还是加上了。

代码

C++
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MOD = 1000007;
const int maxk = 500;
int n, m, k, c[maxk + 10][maxk + 10];

void init(){
    memset(c, 0, sizeof(c));
    c[0][0] = 1;
    for(int i = 0; i <= maxk; i++){
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; j++){
            c[i][j] = (c[i -1][j] + c[i - 1][j - 1]) % MOD;
        }
    }
}
int main(int argc, const char * argv[]) {
    //freopen("/Users/zhangjiatao/Desktop/input.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    init();
    for(int t = 1; t <= T; t++){
        scanf("%d%d%d", &n, &m, &k);
        int sum = 0;
        for(int s = 0; s < 16; s ++){
            int r = n, l = m, b = 0;
            if(s & (1 << 3)) {r--; b++;}
            if(s & (1 << 2)) {r--; b++;}
            if(s & (1 << 1)) {l--; b++;}
            if(s & (1 << 0)) {l--; b++;}
            if(b & 1){
                sum = (sum + MOD - c[r * l][k]) % MOD;
            }
            else{
                sum = (sum + c[r * l][k]) % MOD;
            }
            //cout <<b<<","<<s<<","<< sum << endl;
        }
        printf("Case %d: %d\n", t, sum);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值