来自:http://blog.csdn.net/linyunzju/article/details/7729774
问题:
1. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近。
2. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组之和最接近。
1. 解法1:
由于对两个子数组和最接近的判断不太直观,我们需要对题目进行适当转化。我们知道当一个子数组之和最接近原数组之和sum的一半时,两个子数组之和是最接近的。所以转化后的题目是:从2n个数中选出任意个数,其和尽量接近于给定值sum/2。
这个问题存储的是从前k个数中选取任意个数,且其和为s的取法是否存在dp[k][s]。之所以将选出的数之和放在下标中,而不是作为dp[k]的值,是因为那种做法不满足动态规划的前提——最优化原理,假设我们找到最优解有k个数p1p2...pk(选出的这k个数之和是最接近sum/2的),但最优解的前k-1个数p1p2...pk-1之和可能并不是最接近sum/2的,也就是说可能在访问到pk之前有另一组数q1q2....qk-1其和相比p1p2...pk-1之和会更接近sum/2,即最优解的子问题并不是最优的,所以不满足最优化原理。因此我们需要将dp[k]的值作为下标存储起来,将这个最优问题转化为判定问题,用带动态规划的思想的递推法来解。
外阶段:在前k1个数中进行选择,k1=1,2...2*n。
内阶段:从这k1个数中任意选出k2个数,k2=1,2...k1。
状态:这k2个数的和为s,s=1,2...sum/2。
决策:决定这k2个数的和有两种决策,一个是这k2个数中包含第k1个数,另一个是不包含第k1个数。
dp[k][s]表示从前k个数中取任意个数,且这些数之和为s的取法是否存在。
- #include
- #include
-
- using namespace std;
-
- #define MAXN 101
- #define MAXSUM 100000
- int A[MAXN];
- bool dp[MAXN][MAXSUM];
-
-
- int main()
- {
- int n, i, k1, k2, s, u;
- cin >> n;
- for (i=1; i<=2*n; i++)
- cin >> A[i];
- int sum = 0;
- for (i=1; i<=2*n; i++)
- sum += A[i];
- memset(dp,0,sizeof(dp));
- dp[0][0]=true;
-
- for (k1=1; k1<=2*n; k1++)
- {
- for (k2=k1; k2>=1; k2--)
- for (s=1; s<=sum/2; s++)
- {
-
-
- if (s>=A[k1] && dp[k2-1][s-A[k1]])
- dp[k2][s] = true;
- }
- }
-
-
- for (k1=2; k1<=2*n; k1++)
- for (s=1; s<=sum/2; s++)
- if (dp[k1-1][s]) dp[k1][s]=true;
-
- for (s=sum/2; s>=1 && !dp[2*n][s]; s--);
- printf("the differece between two sub array is %d\n", sum-2*s);
- }
解法2:
由于题目不限制子数组的元素个数,限制条件少,可以进行优化。实际上解法1的思路主要是为了题目2做铺垫,使得题目2的解法不至于太难理解。该题实际上有更简单的解法,该解法的思路和0-1背包问题的思路是一样的。
- #include
- using namespace std;
-
- #define MAXN 101
- #define MAXSUM 100000
- int A[MAXN];
- bool dp[MAXN][MAXSUM];
-
-
- int main()
- {
- int k, s, u, i, n;
- cin >> n;
- for (i=1; i<=2*n; ++i)
- cin >> A[i];
- int sum = 0;
- for (i=1; i<=2*n; ++i)
- sum += A[i];
- dp[0][0] = true;
-
- for (k=1; k<=2*n; ++k)
-
- for (s=0; s<=(sum>>1); ++s)
- {
-
- if (s>=A[k])
- dp[k][s] = dp[k-1][s-A[k]] || dp[k-1][s];
- else
- dp[k][s] = dp[k-1][s];
- }
- for (s=(sum>>1); s>=1 && !dp[2*n][s]; --s);
- cout << sum-2*s;
- }
-
2. 解法:
但本题还增加了一个限制条件,即选出的物体数必须为n,这个条件限制了内阶段k2的取值范围,并且dp[k][s]的含义也发生变化。这里的dp[k][s]表示从前k个数中取k个数,且k不超过n,且这些数之和为s的取法是否存在。
- #include
- #include
-
- using namespace std;
-
- #define MAXN 101
- #define MAXSUM 100000
- int A[MAXN];
- bool dp[MAXN][MAXSUM];
-
-
- int main()
- {
- int n, i, k1, k2, s, u;
- cin >> n;
- for (i=1; i<=2*n; i++)
- cin >> A[i];
- int sum = 0;
- for (i=1; i<=2*n; i++)
- sum += A[i];
- memset(dp,0,sizeof(dp));
- dp[0][0]=true;
-
-
- for (k1=1; k1<=2*n; k1++)
- for (k2=min(k1,n); k2>=1; k2--)
- for (s=1; s<=sum/2; s++)
-
- if (s>=A[k1] && dp[k2-1][s-A[k1]])
- dp[k2][s] = true;
-
- for (s=sum/2; s>=1 && !dp[n][s]; s--);
- printf("the differece between two sub array is %d\n", sum-2*s);
- }
阅读(1145) | 评论(0) | 转发(0) |