离散数学笔记,基础数学分支

1、给定n个数字,要求选出3个数,其和为3的倍数。

思路,分类,把每个数字%3后,0、1、2、的分成三类,然后按类计算。

①、三个数来自同一个类,②、来自0、1、2各一类。

2、将r个相同的球放到n个不同的盒子里面,盒子的球数不限,求方案数。

将r个球用0表示,就是000000....000,然后分成n份,所以需要插上n - 1刀,所以就是r + n - 1长度的01串中,其中1的个数是n - 1个,求有多少种。就是C(n + r - 1, n - 1)。比如分成两份,如果是100的话,就是第一份个数是0,第二份个数是2.

这题也可以这样想,用xi 表示第i个盒子中的球数,其中xi >= 0。所以就是x1 + x2 + x3 + .... + xn = r的解的个数。思路和上面一样。

3、求解x1 + x2 + x3 + x4 = 10的个数,其中xi >= ai

每个数都有权值了,但是明显可以把每个数都减去自身的权值,又回到xi >= 0的解法。

4、求解x1 + x2 + x3 + x4 <= 10。其中xi >= ai

由于是小于等于,那么可以去掉权重后,加多一个辅助变量x5

变成x1 + x2 + x3 + x4 + x5 = 10的解的方案数。为什么呢?

比如1 + 1 + 1 + 1 = 4 < 10是成立的,然后剩下的就相当于给x5了,x5 = 6,然后就是相加等于10的方案数。

变形一下:在一排有20本书的书架上,选出6本书,要求这6本书不能相邻,求方案数。

设xi表示第i本书与上一本书的间隔书的数目,为了满足条件。x1 >= 1, 其他xi >= 2。

那么就是要x1 + x2 + x3 + x4 + x5 + x6 <= 20

变形两下:有5种不同口味的甜甜圈,选出12个,每种选出方法不限。有多少种选法。

0010101000000100,用1把12分成5份,只需要4个1,所以就是询问长度为n + 4的串中,其中1的个数一定要是4个,有多少种这样的串?C(n + 5 - 1, 5 - 1)种。就是选出4个位置放1就可以。如果选出的位置是1、2、3、4,那么就是1111000000000000

就是全部都选了第5种口味。

5、count how many hello world

for (int i = 1; i <= n; ++i) 

  for (int j = 1; j <= i; ++j) 

    for (int k = 1; k <= j; ++k)

      cout << "hello world" << endl;

输出运行了多少次hello world

考虑大小关系,n >= i >= j >= k >= 1

①、如果i和j和k不能相等,那么就是在1、2、3、4、5、6、7....n里,选3个数出来就好了C(n, 3)

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j < i; ++j) {
            for (int k = 1; k < j; ++k) {
                ++ans;
            }
        }
    }

C(n, 3)的意思就是求满足k < j < i的三元组的数目,比如k = 1, j = 2, i = 3的时候统计一次,然后i = 4又统计一次。2、3、4又统计一次。以此类推

但是这里可以相等。

 注意这题和上面那题甜甜圈对比下。

现在我们用0表示ijk,现在是一种合法的情况。那么就相当于在这5个位置中,放合法情况。就是有n - 1 + 3长度的01串,其中0的个数是3个的时候的解。C(n - 1 + 3, 3)

这题还有另一种解法,和取书本的解法一样。

设xi表示第i个数与上一个数的间隔是多少。

x1 = 1(因为从1开始),x2 = 0(因为可以相等),x3 = 0。所以就是x1 + x2 + x3 <= n的方案数。

当x1 = n的时候,就是等式 = n的时候,所以间距是有可能等于n的

还可以,分析成:设xi表示i这个数字被选了多少次,

x1 + x2 + ..... + xn = 3

https://www.hackerrank.com/contests/101hack41/challenges/arias-loops

这一题和以前的不同,因为它的循环是从上一个 + k - 1开始的。

那么就是,设xi表示第i个数与上一个数的间隔是多少。x1 = 1, x2 = 1, x3 = 2, x4 = 3 ..... 

所以就是x1 + x2 + .... xk <= n

公式是C(n - 1 - (k * k - 3 * k) / 2, k);, 因为是求解 <= k的,所以需要一个辅助变量,所以要切k刀

 6、

首先来说一下第一种方法,直接计算,设y1 = a1 - x1 -1,y2 = a2 - x2 - 1。

那么y1 >= 0,y2 >= 0,所以y1 + y2 = a1 + a2 - (x1 + x2) - 2

而且x1 + x2 = n,则y1 + y2 = a1 + a2 - n - 2。转化为上面的解。

因为y1和y2是x1和x2的函数,解出一对y1, y2,就会对应着x1, x2。因为a1和a2都是常数。

这里对x1和x2没什么要求,就是可以是负数,

因为x1 + x2 = 0,x1 < 4 x2 < 4,7组,

-1 + 1 。。 -2 + 2。。-3 + 3。。(调转) 0 + 0

7、补充个小知识,将n个糖果,分成m分,有多少种(保证没一份至少一个)

ans:C(n - 1, m - 1)

先把n个糖果排成一列,然后有n - 1个空格,需要把这n个糖果分成m分的话,需要m - 1刀。所以就是C(n - 1, m - 1)

选出m - 1条红色的线。

8、

将m分成n份数字相乘,有多少种分法?

就是35分成2分的话,1 * 35 。35 * 1。5 * 7。7 * 5

考虑把分解质因数

http://www.cnblogs.com/liuweimingcprogram/p/6106464.html

9、

求方程

3 * x + 5 * y + 7 * z <= m

的解的个数,其中x、y、z都是大于等于0的

这是一题完全背包的问题,对于每一个数字,都可以取x种,

所以设第dp[v]表示产生这个数字的方案数,叠加上去即可。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e3 + 20;
int dp[maxn];
int tans;
int a[] = {0, 3, 5, 7, 11};
void dfs(int cur, int curnum) {
    if (curnum > 500) return;
    if (cur == 5) {
        tans++;
        return;
    }
    for (int i = 0; i <= (500 - curnum) / a[cur]; ++i) {
        dfs(cur + 1, curnum + a[cur] * i);
    }
}
void work() {
    dp[0] = 1;
    for (int i = 1; i <= 4; ++i) {
        for (int j = a[i]; j <= 500; ++j) {
            dp[j] += dp[j - a[i]];
        }
    }
    int ans = 0;
    for (int i = 0; i <= 500; ++i) {
        ans += dp[i];
    }
    cout << ans << endl;
    dfs(1, 0);
    cout << tans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 这个有一个原题,不过太难了,我不会的。m值是10^100

https://www.codechef.com/problems/CHANGE

10、

求解排列,有a个0, b个1,求排列数目,使得连续的0不能超过k1个,连续的1不能超过k2个

思路:dp递推

dp[i][j][k][h]表示放了i个0,j个1,而且连续的0有k个,连续的1有h个

所以:dp[i + 1][j][k + 1][0] += dp[i][j][k][h]

http://www.cnblogs.com/liuweimingcprogram/p/6337149.html

http://acm.hdu.edu.cn/showproblem.php?pid=5514

有关因子的容斥

给出n个数字,然后求出在1--m的范围内,有多少个数字是a[i]的倍数。

正常的做法是2^n的,但是考虑到这里的n个数字只能是m的因数。LCM也只能全部是因子数那里出现

那么就有了一种全新的容斥方法。

需要解决的只是:和一般的容斥一样。重复的就减去。这里有一个快速知道加减的方法

比如m = 24

1、2、3、4、6、8、12、24

给定的n个数字是:2、3、6

套路就是:

把容斥需要的系数(正或者负,还有多少倍),表达成两个数的差。

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
vector<int> vc;
const int maxn = 1e4 + 20;
int vis[maxn], num[maxn];
void work() {
    memset(vis, false, sizeof vis);
    memset(num, false, sizeof num);
    vc.clear();
    int n, m;
    scanf("%d%d", &n, &m);
    int en = (int)sqrt(m * 1.0);
    for (int i = 1; i <= en; ++i) {
        if (m % i == 0) {
            if (i * i == m) vc.push_back(i);
            else {
                vc.push_back(m / i);
                vc.push_back(i);
            }
        }
    }
    sort(vc.begin(), vc.end());
    vc.pop_back();
    for (int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        int res = __gcd(x, m);
        for (int j = 0; j < vc.size(); ++j) {
            if (vc[j] % res == 0) vis[j] = true;
        }
    }
    LL ans = 0;
    for (int i = 0; i < vc.size(); ++i) {
        if (vis[i] != num[i]) {
            LL t = (m - 1) / vc[i];
            ans += t * (t + 1) / 2 * vc[i] * (vis[i] - num[i]);
            for (int j = i + 1; j < vc.size(); ++j) {
                if (vc[j] % vc[i] == 0) num[j] += vis[i] - num[i];
            }
        }
    }
    static int f = 0;
    printf("Case #%d: %I64d
", ++f, ans);
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    int t;
    scanf("%d", &t);
    while (t--) work();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/liuweimingcprogram/p/6091396.html