整数数列 a,先输入 a 的元素个数 n,再输入整数数列 a 的各元素 (数组 a 中元素个数不超过 10 个),寻找是否有这样一种划分: 将 a 中元素分为两组,使得这两组中的元素之和相等。若可以找到,输出 True, 并写出一种划分 (输出格式见样例); 若不能,输出 False。
样例 1:
输入:71 2 3 4 5 6 9
输出:True
(6.9)(1,2,3,4,5)
样例 2:
输入:31 3 5
输出:False
题目介绍:需要对一个输入的数组尝试划分。这种题目可以采取动态规划方法计算。
整体思路:计算出整个数列各项和(sum)。如果是奇数,则不可能平分,直接输出“False”;如果是偶数,则要凑出一个子列,使其各项和为sum的一半(half)。
细节方法:假定输入的数列升序排列(若不是,可以先排序)。选定最后一位(last),向前检索,根据现有求和(temp)和目标和(half)的大小来决定是否加入正在检索的一位。整个数列检索后,若成功,输出“True”和划分方式;若不成功,前移最后一位last所在位置,再次检索。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int a[15];//输入的数列
int l[15];//其中一个划分部分
int r[15];//剩下的部分
int main(void) {
int N;//数列里数的个数
int n = 0;//第一个部分数的个数
int m = 0;//第二个部分数的个数
int key = 0;//判断是否可行
int sum = 0;//数列求和
int half = 0;//求和值得一半
int temp = 0;//当前求得和值
cin >> N;
for (int i = 0; i < N; i++) {
cin >> a[i];
sum += a[i];
}//按照题目要求输入
if (sum % 2 != 0)
key = -1;//如果数列求和,和值是奇数,无论如何不能划分
else {
half = sum / 2;
//假定所有的数字按照升序排列;如果不是,那就自己给他排一下
for (int j = N - 1; j >= 1; j--) {
temp = a[j];//制定一个最后一位,假定划分中包含这个最后一位,检验是否可行
for (int k = j - 1; k >= 0; k--) {
if (temp == half) {
key = 1;
break;//划分成功,结束
}
else if (temp < half) {
temp += a[k];
l[n] = k;
n++;
//当前值比和值一半小,那就加上这一位的数字,并把这个数字的位置记录在了l[n]划分部分里;
}
else {
if (temp == a[j])
break;//说明最后一位大于和值的一半,不可能能够划分;
else {
temp -= a[k + 1];
temp += a[k];
n--;
l[n] = k;
n++;
}
//当前值比和值一半大,那就减去上一个数字,加上当前位置数字,并把这个数字的位置替换上一个数字的位置
}
}
if (key == 1) {
l[n] = j;
break;
//如果成功,记录下选定的最后一位的位置
}
}
}
if (key == 1) {
int p = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j <= n; j ++ ) {
if (i != l[j]) {
continue;
}
else {
p = -1;
break;
}
}
if (p == 0) {
r[m] = i;
m++;
}
//划分成功,则把了l[n]这一部分中没有的数字位置记录给r[m];
}
cout << "True" << "\n";
cout << '(';
for (int i = n - 1; i >= 0; i--) {
cout << a[l[i]];
cout << ',';
}
cout << a[l[n]] << ')' << "\n";
cout << '(';
for (int i = 0; i < m - 1; i++) {
cout << a[r[i]];
cout << ',';
}
cout << a[r[m - 1]] << ')';
}
else
cout << "False";
return 0;
}