zoukankan      html  css  js  c++  java
  • 离散数学笔记,基础数学分支

    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
  • 相关阅读:
    史上世界上最惨烈的几次股灾!
    做网站的人必须遵守的N大定律,事实上不止做网站
    看士兵突击有感
    中美小学生守则比较
    奥运赞助商及合作伙伴
    sharepoint 备份还原
    sql事务
    javascript 命名空间 类 继承 重载 私有成员和公开成员
    XSLT 学习一
    Web版OutLook,利用POP接收邮件服务器邮件
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/6091396.html
Copyright © 2011-2022 走看看