首页 > 编程语言 >算法竞赛中的小球放盒子问题

算法竞赛中的小球放盒子问题

时间:2022-10-31 16:26:08浏览次数:75  
标签:盒子 int res sp 小球 long 算法 dp

背景:写题的时候遇到过很多关于这类问题的变种题,所以打算总结一下

问题分类

根据球是否不同,盒子是否不同,盒子是否为空,一共可以分为 \(2^{3}\) 种情况讨论

Problem 1

题意

给定 N 个不同的球,放进 M 个不同的盒子,盒子允许为空,有多少种方案?

解析

从每个球的角度出发,每个球都有M种放法,所以就是 \(M^{N}\)

题目链接

Problem 1

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n,m;
	while(cin >> n >> m)
	cout << qmi(m,n) << endl;
	return 0;
}

Problem 2

题意

给定 N 个相同的球,放进 M 个不同的盒子,盒子不允许为空,有多少种方案?

解析

这个就是经典的隔板法问题,相当于把N个球用隔板分成M个部分
N个球有N-1个空隙,在空隙中选择M-1个作为隔板将原来分成M个部分,所以答案是 \(C_{N-1}^{M-1}\)

题目链接

Problem 2

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int sp[20];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}
int C(int a,int b){
    if(b > a) return 0;
    int p = sp[a];
    int q = sp[b];
    int r = sp[a - b];
    int res = p / q;
    return res / r;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    sp[0] = 1;
    for(int i = 1; i <= 15; i++) sp[i] = sp[i-1] * i;
    while(cin >> n >> m){
        cout << C(n-1,m-1) << endl;
    }
    return 0;
}

Problem 3

题意

给定 N 个相同的球,放进 M 个不同的盒子,盒子允许为空,有多少种方案?

解析

和问题2类似,只需要把隔板法变形一下。
我们可以假定每个隔板的右边自带了一个小球,那么这样总共就会有 N+M 个小球,选 M-1 个空隙,答案就是 \(C_{M+N-1}^{M-1}\)
每次插入板子后形成的组合,把右边的球去掉,可以发现就是我们所需要的答案

题目链接

Problem 3

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int sp[20];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}
int C(int a,int b){
    if(b > a) return 0;
    else return sp[a] / sp[b] / sp[a-b];
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    sp[0] = 1;
    
    for(int i = 1; i <= 20; i++) sp[i] = sp[i-1] * i;
    while(cin >> n >> m){
            cout << C(n+m-1,n) << endl;

    }
    return 0;
}

Problem 4

题意

给定 N 个相同的球,放进 M 个相同的盒子,盒子允许为空,有多少种方案?

解析

利用动态规划解决,设 \(dp[i][j]\) 表示将i个小球放到j个盒子里的方案数,那么初始化显然有 \(dp[0][0] = dp[0][1] = ...... = dp[0][m] = 1\)
转移分两种情况考虑,第一种就是允许有盒子为空的情况 那么就是 \(dp[i][j-1]\)
第二种是不允许有空盒, 此时可以假定j个盒子里每个盒子已经有了一个球(或者可以理解为把每个盒子抽一个球之后再放回去)
这种方案数等价于 \(dp[i-j][j]\)
所以转移方程就是 \(dp[i][j] = dp[i][j-1] + dp[i-j][j]\)

题目链接

Problem 4

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int sp[20];
int dp[20][20];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}
int C(int a,int b){
    if(b > a) return 0;
    else return sp[a] / sp[b] / sp[a-b];
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    sp[0] = 1;
    for(int i = 1; i <= 20; i++) sp[i] = sp[i-1] * i;
    for(int i = 1; i <= 15; i++) dp[0][i] = 1;
    for(int i = 1; i <= 15; i++){
        for(int  j = 1; j <= 15; j++){
            if(i >= j)
                dp[i][j] = dp[i][j-1] + dp[i-j][j];
            else dp[i][j] = dp[i][j-1];
        }
    }
    while(cin >> n >> m){
            cout << dp[n][m] << endl;
    }
    return 0;
}

Problem 5

题意

给定 N 个相同的球,放进 M 个相同的盒子,盒子不允许为空,有多少种方案?

解析

跟第4个问题几乎一样,转移方程也一样,只不过这次变成了盒子不能为空
那么最后的答案就是 \(dp[n-m][m]\) (即假定每个盒子已经有一个球了)

题目链接

Problem 5

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int sp[20];
int dp[20][20];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}
int C(int a,int b){
    if(b > a) return 0;
    else return sp[a] / sp[b] / sp[a-b];
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    sp[0] = 1;
    for(int i = 1; i <= 20; i++) sp[i] = sp[i-1] * i;
    for(int i = 1; i <= 15; i++) dp[0][i] = 1;
    for(int i = 1; i <= 15; i++){
        for(int  j = 1; j <= 15; j++){
            if(i >= j)
                dp[i][j] = dp[i][j-1] + dp[i-j][j];
            else dp[i][j] = dp[i][j-1];
        }
    }
    while(cin >> n >> m){
        if(n >= m)
            cout << dp[n-m][m] << endl;
        else cout << 0 << endl;
    }
    return 0;
}

Problem 6

题意

给定 N 个不同的球,放进 M 个相同的盒子,盒子不允许为空,有多少种方案?

解析

依然采用动态规划解决,设 \(dp[i][j]\) 表示将i个小球放到j个盒子里的方案数
考虑两种情况,第一种情况,多了一个空盒子,多出来的球就放这个空盒子里,即 \(dp[i-1][j-1]\)
第二种情况,多出来的球放到之前的盒子里,由于有j个相同的盒子,即 \(dp[i-1][j] \times j\)
所以转移方程就是从 \(dp[i][j] = dp[i-1][j-1] + dp[i-1][j] \times j\)
其实这个是第二类斯特林数,存在通项公式可以优化时间复杂度,这里不再赘述,有兴趣的可以自己下来研究一下

题目链接

Problem 6

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int dp[20][20];
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    dp[0][0] = 1;
    for(int i = 1; i <= 15; i++){
        for(int j = 1; j <= 15; j++){
            dp[i][j] = dp[i-1][j-1] + j * dp[i-1][j];
        }
    }
    while(cin >> n >> m){
        cout << dp[n][m] << endl;
    }   
    return 0;
}

Problem 7

题意

给定 N 个不同的球,放进 M 个相同的盒子,盒子允许为空,有多少种方案?

解析

跟第6个问题一样,答案抽象出来其实是 \(\sum_{i=1}^ndp[n][i]\)
因为这里的区别就是有盒子可以为空,那么在第6个问题中 \(dp[n][j]\) 表示成把n个球放进j个盒子里的方案数,并且盒子不为空
其实在这个问题中就等价于有j个盒子不为空,而另外m-j个盒子为空的情况

题目链接

Problem 7

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int dp[20][20];
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    dp[0][0] = 1;
    for(int i = 1; i <= 15; i++){
        for(int j = 1; j <= 15; j++){
            dp[i][j] = dp[i-1][j-1] + j * dp[i-1][j];
        }
    }
    while(cin >> n >> m){
        int res = 0;
        for(int i = 1; i <= m; i++){
            res += dp[n][i];
        }
        cout << res << endl;
    }   
    return 0;
}

Problem 8

题意

给定 N 个不同的球,放进 M 个不同的盒子,盒子不允许为空,有多少种方案?

解析

可以先假定盒子都相同,问题转化为问题6
然后考虑不同盒子的排列即可
最后答案是 \(M! \times dp[n][m]\)

题目链接

Problem 8

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int dp[20][20];
int sp[20];
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    sp[0] = 1;
    for(int i = 1; i <= 15; i++) sp[i] = sp[i-1] * i;
    dp[0][0] = 1;
    for(int i = 1; i <= 15; i++){
        for(int j = 1; j <= 15; j++){
            dp[i][j] = dp[i-1][j-1] + j * dp[i-1][j];
        }
    }
    while(cin >> n >> m){
        cout << sp[m] * dp[n][m] << endl;
    }   
    return 0;
}

标签:盒子,int,res,sp,小球,long,算法,dp
From: https://www.cnblogs.com/Sun-Wind/p/16844723.html

相关文章

  • Diff算法(面试)
    Diff算法探讨的就是虚拟DOM树发生变化后,生成DOM树更新补丁的方式。对比新旧两株虚拟DOM树的变更差异,将更新补丁作用于真实DOM,以最小成本完成视图更新。 具体流......
  • 第四届全国大学生算法设计与编程挑战赛(秋季赛)正式赛题解
    没时间写题解了,随便写两笔吧,看不懂可以联系QQ160042137901(Easy)直接暴力枚举每个状态及其所有转移,时间复杂度\((T2^nn^2)\)。02(Easy)二分答案,用一个单调队列或者优先......
  • 算法导论(第23章 最小生成树)
    目录23.1最小生成树的形成23.2\(Kruskal\)算法和\(Prim\)算法\(Kruskal\)算法\(Prim\)算法问题描述:对于一个连通无向图\(G=(V,E)\),为其每条边\((u,v)\inE\),赋予权......
  • Java算法基础 - 单链表详解(文末有配套视频)
    导航​​步骤1只用Java类能实现吗?​​​​步骤2类里面有顾客属性​​​​步骤3排队打饭​​​​步骤4从一个顾客联系到另一个顾客​​​​步骤5加一个next字段​......
  • 力扣HOT100算法题5:最长回文字串
    文章目录​​一、题目​​​​二、方法一:解题思路​​​​三、方法一:代码解析​​​​四、方法二:动态规划​​​​五、方法二:代码解析​​一、题目给你一个字符串s,找到s......
  • 实验二 逻辑回归算法实验
    【实验目的】理解逻辑回归算法原理,掌握逻辑回归算法框架;理解逻辑回归的sigmoid函数;理解逻辑回归的损失函数;针对特定应用场景及数据,能应用逻辑回归算法解决实际分类问题。......
  • 机器学习的发展(初级算法梳理一)
    2016年3月,阿尔法围棋与围棋世界冠军、职业九段棋手李世石进行围棋人机大战,以4比1的总比分获胜.深度学习开始进行大众的视野中.深度学习其实是机器学习的一个分支,我们今天......
  • 【计算机视觉(CV)】sklearn之分类算法与手写数字识别
    【计算机视觉(CV)】sklearn之分类算法与手写数字识别作者简介:在校大学生一枚,华为云享专家,阿里云专家博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产......
  • 前向算法
    A=[[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]]B=[[0.5,0.5],[0.4,0.6],[0.7,0.3]]pi=[0.2,0.4,0.4]defa1():t=0a=[]......
  • 维特比算法
    #状态转移矩阵A=[[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]]#观测概率矩阵B=[[0.5,0.5],[0.4,0.6],[0.7,0.3]]#观测序列pi=[0.2,......