离散数学笔记,基础数学分支
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; }
这个有一个原题,不过太难了,我不会的。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\n", ++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; }
posted on 2016-11-22 22:10 stupid_one 阅读(1640) 评论(0) 编辑 收藏 举报