求和输出问题_c++

整数拆分

  • 动态规划问题之一

一个数为N,求在数列0~m之间能相加为N的可能

可能的种类数

a [ n ] [ m ] 两 个 对 立 的 可 能 性 { d p [ n − 1 ] [ m − 1 ] : 存 在 1 的 情 况 ( 两 边 都 减 一 , 去 除 一 个 1 ) d p [ n − m ] [ m ] : 不 存 在 1 的 情 况 a[n][m]两个对立的可能性 \begin{cases} dp[n-1][m-1] : 存在1的情况(两边都减一,去除一个1)\\ dp[n-m][m] : 不存在1的情况 \end{cases} a[n][m]{dp[n1][m1]:1(1)dp[nm][m]:1
故 :   d p [ n ] [ m ] = d p [ n − 1 ] [ m − 1 ] + d p [ n − m ] [ m ] 故:\ dp[n][m]=dp[n-1][m-1]+dp[n-m][m]  dp[n][m]=dp[n1][m1]+dp[nm][m]

//n是输入的数字(和),m是1~m的可能
int CountKind(int m, int n)
{

	//	二维数组a[n][m]=a[n-1][m-1]+a[n-m][m]
	//从a[1][1]开始,因为0没有意义,全部为0就行
	//初始化第一种情况,就是m=1的时候,1个数字就够了
	for (int i = 1; i < n; i++)
	{
		arr[i][1] = 1;
	}
	//n<m的时候,为0,没有可能。
	for (int i = 2; i <= n; i++)
	{
		//n<m是没有意义,不做
		for (int j = 1; j <= i; j++)
		{
			arr[i][j] = arr[i - 1][j - 1] + arr[i - j][j];
		}
	}

	//把数组输出
	for (int i = 0; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
		{
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}

	//把a[][m]的每一项都加起来,最后一项 n==m 不要
	int sum = 0;
	for (int i = 1; i <=m; i++)
	{
		//最后一项 n == m 不要
		if (i >= n) break;
		sum += arr[n][i];
	}
	
	return sum;
}

输出所有可能

递归解决

  • 一个数N,可以拆分为当前的i和(N-i);而N-1有spiltNum(N-i)的情况。
  • 一个遍历从i开始,不用担心出现2,8和8,2
//数组至少长度为 num
int arr[101], num;
//n是输入的数字(和),s是拆分的最小的值,x是数组的下标,填到第几位
void splitNum(int n, int s,int x) {
	if (n > 0)
	{
		for (int i = s; i <= n; i++) {
			arr[x] = i;
			//接着拆分剩下的值,i是当前最小的值
			splitNum(n - i, i, x + 1);
		}
		//找完了,结束。
		return;
	}

	//n==0 ,不用直接输出。

	//不是4==4的情况
	if (num != arr[0]) 
		cout << num << '=' << arr[0];
	for (int i = 1; i < x; i++) cout << '+' << arr[i];
		cout << endl;
	
}

//main()函数的调用
main()
{
	cin>>num;
	//最小值从1开始,下标从0开始
	spiltNum(num,1,0);
}

拆分的数据只能是数组A中的

  • 0-1背包问题 【只出现一次】
    a r r [ i ] [ j ] = { 1       ;       j = 0 , 价 值 为 0 的 是 一 种 , 不 放 a r r [ i − 1 ] [ j ]      ;      j < v a l u e [ i ] a r r [ i − 1 ] [ j ] + a r r [ i − 1 ] [ j − v a l u e [ i ] ]     ;      j > = v a l u e [ i ] arr[i][j]= \begin{cases} 1~~~~~;~~~~~j=0,价值为0的是一种,不放\\ arr[i-1][j]~~~~;~~~~j<value[i]\\ arr[i-1][j]+arr[i-1][j-value[i]] ~~~ ;~~~~j>=value[i] \end{cases} arr[i][j]=1     ;     j=0,0arr[i1][j]    ;    j<value[i]arr[i1][j]+arr[i1][jvalue[i]]   ;    j>=value[i]
value[i]i \ n01234567891011
00100000000000
50100001100000
51100002000010
102100002000020
23101002020020
34100103002040

注意这里的下标对应:arr[i][j-1]对应value[i-1]

//n是要达到的重量,len是物品种类
int oneArrayBag(int value[],  int len,int n)
{
	//定义一个数组,长度为n
//	vector<int >arr;
	//初始化为0
	memset(arr, 0, sizeof(arr));
	for (int i = 0; i <= len; i++)
	{
		arr[i][0] = 1;
	}
	for (int i = 1; i <=len;i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (j < value[i-1])
				arr[i][j] = arr[i - 1][j];
			else
			{
				arr[i][j] = arr[i-1][j] + arr[i - 1][j - value[i-1]];
			}
		}
	}
	for (int i = 0; i <= len; i++)
	{
		for (int j = 0; j <= n; j++)
		{
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}
	return arr[len][n];
}

  • 一位数组解决 :第二层从后面遍历,防止覆盖
    a r r [ j ] = { 1       ;       j = 0 , 价 值 为 0 的 是 一 种 , 不 放 a r r [ j ] + a r r [ j − v a l u e [ i ] ]     ;      j > = v a l u e [ i ] arr[j]= \begin{cases} 1~~~~~;~~~~~j=0,价值为0的是一种,不放\\ arr[j]+arr[j-value[i]] ~~~ ;~~~~j>=value[i] \end{cases} arr[j]={1     ;     j=0,0arr[j]+arr[jvalue[i]]   ;    j>=value[i]
int oneArrayBag(int value[],  int len,int n)
{
	//定义一个数组,长度为n
//	vector<int >arr;
	//初始化为0
	memset(arr, 0, sizeof(arr));
	arr[0] = 1;
	for (int i = 1; i <=len;i++)
	{
		for (int j = n; j >=value[i-1]; j--)
		{
				arr[j] = arr[j] + arr[j - value[i-1]];
		}
	}
	for (int i = 0; i <= n; i++)
	{
		cout << i << " ";
	}
	cout << endl;
	for (int j = 0; j <= n; j++)
	{
		cout << arr[j] << " ";
	}
	cout << endl;

	return arr[n];
	
}
  • 完全背包问题 :先把数组中重复的去掉
    • 去重复
int value[] = { 5,5,10,2,3 };
void uniqueArray(int value[])
{
	int len = sizeof(value) / sizeof(*value);
	vector<int>arr;
	for (int i = 0; i < len; i++)
	{
		arr.push_back(value[i]);
	}
	//正序排列
	sort(arr.begin(),arr.end());

	//这是一个删除,找到unique相同的。
	arr.erase(unique(arr.begin(), arr.end()), arr.end());
	
	//这是一个迭代容器输出。
	for (vector<int>::iterator i = arr.begin(); i < arr.end(); i++)
	{
		cout << *i << " ";
	}
}

参考资料

动态规划:求把数n分成若干个正数相加的总情况数
拆分与完全背包问题
求一个数组中和为sum的方法数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值