CCF1303 鸣人的影分身(n个相同的球,放在m个相同的盒子里,且盒子可以为空)

题目链接

思路

典型的HZB不会做的题目

这道题其实跟洛谷的放苹果一模一样,我们可以看作是n个相同的球,放在m个相同的盒子里,且盒子不可以为空进行m次计算,将m,m-1,m-2,m-3……0的值加在一起。因为数据较小,也可以用深搜做。这里采用的是动态规划方法,用递归函数去实现。

我们知道,当查克拉的能量<0时,它的方案数显然为0。(当然,如果在循环中就可以不用这一条,因为C++没有下标为负数的数组,不过这里用的是递归的方法,所以要加上这一条)

先定义一个递归函数f(int m,int n)

当没有查克拉的能量或者只有一个影分身时,它的方案数为1。

当查克拉的能量<分成分身个数时,它的方案数为f[m][m]。

当查克拉的能量>=分成分身个数时,它的方案数相当于f[m-n][n]+f[m][n-1],也就是少了n个能量分成n个分身的方案数+能量不变少一个分身的方案数。

比如有一个3个能量,分成二个分身,它的方案数相当于1个能量分成2个分身+3个能量分成1个分身的方案数。也就是2,分别是1,2和3,0;

动态转移方程:

if (m<0) return 0;

if (m==0||n==1) return 1;

if (m<n) return f(m,m);

if (m>=n) return f(m-n,n)+f(m,n-1);//其实前面的判断可以不要,因为m不是<n就是>=n,这里加上去是为了方便理解。

这就有了后面的代码

#include<cstdio>
#include<cstring>
#define r(i,a,b) for(int i=a;i<=b;i++)//循环
using namespace std;
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}//最大值和最小值函数
int maxn=0,minn=0,ans=0;//我每次做动态规划题都会这样留三个变量。。。
int t,n,m;//t组测试数据,m点能量,n个分身
int f(int m,int n)//前面解释过了
{
	if (m<0) return 0;
	if (m==0||n==1) return 1;
	if (n>m) return f(m,m);
	if (m>=n)return f(m-n,n)+f(m,n-1);
}
void read(int &f)//输入流
{
	f=0;bool d=0;char c;
	while (c=getchar(),c<'0'||c>'9') if (c=='-') d=1;f=f*10+c-48;
	while (c=getchar(),c>='0'&&c<='9') f=f*10+c-48;
	if (d) f*=-1;
}
void write(int x)//输出流
{
	if (x) write(x/10);else return;
	putchar(x%10+48);
}
void sc(int x,int y)//因为输出流没办法输出0,所以就有了这个过程。y为避免输出最后一个换行,其实这个也没用。。。强迫症。。。
{
	if (!x) 
		putchar(48);
	write(x);
	if (t-y) putchar(10);
}
int main()
{
	read(t);
	r(i,1,t)
	{
		read(m);read(n);
		sc(f(m,n),i);
	}
}

递归函数的优势是可以少开一个数组,缺点是速度慢一点,当然也可以加上记忆化,因为它有t组测试数据,这样就可以减少不少的时间,不过要多开一个数组。

#include<cstdio>
#include<cstring>
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int maxn=0,minn=0,ans=0;
int t,n,m;int d[101][101];//保存之前已经算过的结果
int f(int m,int n)
{
	if (d[m][n]) return d[m][n];//如果这个点已经有值了直接返回
	if (m<0) return 0;
	if (m==0||n==1) {d[m][n]=1;return 1;}
	if (n>m) {int t=f(m,m);d[m][n]=t;return t;};
	if (m>=n){int t=f(m-n,n)+f(m,n-1);d[m][n]=t;return t;}
}
void read(int &f)
{
	f=0;bool d=0;char c;
	while (c=getchar(),c<'0'||c>'9') if (c=='-') d=1;f=f*10+c-48;
	while (c=getchar(),c>='0'&&c<='9') f=f*10+c-48;
	if (d) f*=-1;
}
void write(int x)
{
	if (x) write(x/10);else return;
	putchar(x%10+48);
}
void sc(int x,int y)
{
	if (!x) 
		putchar(48);
	write(x);
	if (t-y) putchar(10);
}
int main()
{
	read(t);
	r(i,1,t)
	{
		read(m);read(n);
		sc(f(m,n),i);
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值