整数拆分
- 动态规划问题之一
一个数为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[n−1][m−1]:存在1的情况(两边都减一,去除一个1)dp[n−m][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[n−1][m−1]+dp[n−m][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,价值为0的是一种,不放arr[i−1][j] ; j<value[i]arr[i−1][j]+arr[i−1][j−value[i]] ; j>=value[i]
value[i] | i \ n | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
5 | 1 | 1 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 1 | 0 |
10 | 2 | 1 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 2 | 0 |
2 | 3 | 1 | 0 | 1 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 2 | 0 |
3 | 4 | 1 | 0 | 0 | 1 | 0 | 3 | 0 | 0 | 2 | 0 | 4 | 0 |
注意这里的下标对应: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,价值为0的是一种,不放arr[j]+arr[j−value[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 << " ";
}
}