题目链接
题目详情 - 湮灭残昼 - 南阳理工学院OJ (nyist.edu.cn)
太痛了太痛了 第一次遇见错误类型TOO-LATE,赛后学长给我说这题过了,晚交了十秒的痛啊!!!
其实我这题的解题思路就是一个顺着往下想的过程,开赛就打开A上来一个自信dfs交上去 然后wa,在一细看就发现好像不太对,没有考虑0组成数字的情况,此时赛时已经达到了14分钟……
下面就是大致思路,wa掉的dfs不要扔,裹上面包糠,塞进二维数组就能打出来一个由n个正整数组成m的所有可能性,图长这个样
这应该就是一个杨辉三角的样子(后来某某佬提到才意识到原来就是杨辉三角
然后dfs就可以删掉了,改成box[i][j]=box[i-1][j-1]+box[i-1][j]
接下来考虑包含0的情况
假设需要我们求由5个数组成10的所有可能数量,那么在取出box的box[5][10]之后,再考虑由1个0和4个正整数组成10的情况数量,此时只需要求出这一个0在n=5个位置选哪一个即可,即C5 1,五个位置选出一个放0,等于5,5*4个正整数组成10的数量(box[10][4])
然后是由2个0和3个正整数组成10的情况,同理即C5 2五个位置选2个位置放0,等于10,10*box[10][3]
依此类推
而关于Cmn的求法,如果直接求出分子分母再相除必会爆long long,而边乘分子然后除分母则会用到浮点数,就会出现精度问题容易出错(但也能写),所以我们可以选择让分母从小到大乘,分子从大到小乘,就可以得出来结果。
ps:为什么这样乘就不会出现精度问题了?佬们想一想肯定就能想到了,先*1,则分子若存在1的倍数即可消掉分母的1,再乘2,分子出现2的倍数即可消掉,而每两个数必有一个2的倍数,分母*3时分子也必然是连续的三个数,也就能消掉分母的3了
结果大概就是这个情况了,思路可能很麻烦,但是很好理解捏
已ac代码:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<string.h>
#include<stack>
#include<queue>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
ll box[25][25], ans[25][25];
ll c(int m, int n) //组合数的函数
{
ll ans = 1;
for (ll i = 1; i <= n; i++, m--) {
ans *= m;
ans /= i;
}
return ans;
}
int main()
{
for (int i = 1; i <= 20; i++) {
box[i][1] = 1;
ans[i][1] = box[i][1];
}
for (int i = 2; i <= 20; i++) {
for (int j = 2; j <= 20; j++) {
box[i][j] = box[i - 1][j - 1] + box[i - 1][j];
ans[i][j] = box[i][j];
ll k = j, l = 1;
while (k--) {
ans[i][j] += box[i][k]*c(j,l);
l++;
}
}
}
int a, b;
cin>>a>>b;
cout<<ans[a][b];
return 0;
}
by yq