P1655 小朋友的球 题解

文章讲述了使用动态规划解决将球放入盒子的问题,初始状态dp[i][1]=1。随着球数增加,计算过程中出现阶乘级增长导致溢出。尝试使用__int128类型处理大数,但最终仍需采用高精度计算的方法来正确求解。
摘要由CSDN通过智能技术生成

此题算法动态规划

令dp[i][j]表示将前i个球放入j个盒子中,显然dp[i][1]=1

若j不等于1,dp[i][j]可以转化为:将前i-1个球放入j个盒子里,再把第i个球任意放入一个盒子里的数量,加上将前i-1个球放入j-1个盒子里,再把第i个球放入剩下的那个盒子里的数量

于是dp[i][j]=dp[i-1][j-1]+j*dp[i-1][j]

于是可写出代码:

#include <iostream>
using namespace std;
const int N=105;
int dp[N][N];
int n,m;
int main()
{
	for(int i=1;i<=100;i++)
	{
		for(int j=1;j<=100;j++)
		{
			if(j==1) dp[i][j]=1;
			else dp[i][j]=dp[i-1][j-1]+dp[i-1][j]*j;
		}
	}
	while(cin>>n>>m) cout<<dp[n][m]<<"\n";
	return 0;
}

交上去,我们就开心地得到了20分

为什么呢?

我们发现dp[i][j]那里有一个dp[i-1][j]j,证明dp[i][j]的数量是阶乘级增长的,不溢出才怪

于是,我们也许会尝试用__int128骗点分:

#include <iostream>
using namespace std;
const int N=105;
__int128 dp[N][N];
int n,m;
void write(__int128 x)//__int128没有自己的输入输出,所以我们要自己写
{
	int ans[1005]={0},top=0;
	do
	{
		ans[top++]=x%10;
		x/=10;
	}while(x);
	while(top)
	{
		putchar(ans[--top]+'0');
	}
}
int main()
{
	for(int i=1;i<=100;i++)
	{
		for(int j=1;j<=100;j++)
		{
			if(j==1) dp[i][j]=1;
			else dp[i][j]=dp[i-1][j-1]+dp[i-1][j]*j;
		}
	}
	while(cin>>n>>m)
	{
		write(dp[n][m]);
		cout<<"\n";
	}
	return 0;
}

50分,看来,我们还是得用烦人的高精度,具体实现见代码注释

#include <iostream>
using namespace std;
const int N=105;
int n,m;
struct Node
{
	int num[1000];//存数 
	int len;//存长度,方便便利 
}dp[N][N];
void write(Node x)
{
	for(int i=x.len;i>=1;i--) cout<<x.num[i];//由于数是倒着存的,所以要倒着输出 
}
Node Add(Node A,Node B)
{
	Node ans={0};//存计算出来的结果 
	int len=max(A.len,B.len);
	for(int i=1;i<=len;i++)
	{
		ans.num[i]+=A.num[i]+B.num[i];//相加 
		if(ans.num[i]>=10)
		{
			//逢十进一 
			ans.num[i+1]++;
			ans.num[i]%=10;
			if(i==len) len++;
		}
	}
	//记录长度 
	ans.len=len;
	return ans;
}
Node Mul(Node A,int B)
{
	int len=A.len;
	Node ans={0};//记录计算出来的结果 
	for(int i=1;i<=len;i++)
	{
		ans.num[i]+=A.num[i]*B;//相乘
		//进位 
		ans.num[i+1]+=ans.num[i]/10;
		ans.num[i]%=10;
	}
	//如果还需继续进位,则继续进位 
	while(ans.num[len+1]>0)
	{
		len++;
		ans.num[len+1]=ans.num[len]/10;
		ans.num[len]%=10;
	}
	ans.len=len;//记录长度 
	return ans;
}
int main()
{
	//动态规划部分 
	for(int i=1;i<=100;i++)
	{
		for(int j=1;j<=100;j++)
		{
			if(j==1)
			{
				dp[i][j].len=1;
				dp[i][j].num[1]=1;
			}
			else dp[i][j]=Add(dp[i-1][j-1],Mul(dp[i-1][j],j));
		}
	}
	//输出部分 
	while(cin>>n>>m)
	{
		if(n<m) cout<<0;//n<m需特判 
		write(dp[n][m]);
		cout<<"\n";
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值