晓萌希望将1到N的连续整数组成的集合划分成两个子集合,且保证每个集合的数字和是相等。例如,对于N=3,对应的集合{1,2,3}能被划分成{3} 和 {1,2}两个子集合.
这两个子集合中元素分别的和是相等的。
对于N=3,我们只有一种划分方法,而对于N=7时,我们将有4种划分的方案。
输入包括一行,仅一个整数,表示N的值(1≤N≤39)。
输出包括一行,仅一个整数,晓萌可以划分对应N的集合的方案的个数。当没发划分时,输出0。
样例1
输入:
7
输出:
4
注:动态规划
dp[i][j] 表示在区间[1,i]之间,和为j的子集的个数。状态转移方程如下:
(1) dp[i][j] = dp[i-1][j] ; (j < i)
(2) dp[i][j] = dp[i-1][j] + 1 ; (j == i)
(3) dp[i][j] = dp[i-1][j] + dp[i-1][j-i] ; (j > i)
#include<iostream>
using namespace std;
long long dp[40][40 * (40 + 1) / 2];
int main() {
int n;
cin >> n;
int half = n * (n + 1) / 4;//集合一半的和
if (n * (n + 1) % 4 != 0) {
cout << 0 << endl;
return 0;
}
dp[1][1] = 1;
for (int i = 2; i <= n; i++)
for (int j = 1; j <= half; j++)
if (j < i)
dp[i][j] = dp[i - 1][j];
else if (j == i)
dp[i][j] = dp[i - 1][j] + 1;
else
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - i];
cout << dp[n][half] / 2 << endl;
return 0;
}
优化:
#include<iostream>
using namespace std;
const int maxn = 10000;
long long f[100000];
int n, s;
int main() {
cin >> n;
s = n * (n + 1); //1~n的和的2倍
if (s % 4 != 0) { //不能进行划分
cout << 0 << endl;
return 0;
}
s /= 4;
f[0] = 1;
for (int i = 1; i <= n; i++)
for (int j = s; j >= i; j--)
f[j] += f[j - i];
cout << f[s] / 2 << endl;
return 0;
}
枚举:
(此法超时,只供理解)
#include<iostream>
#include<cstring>
using namespace std;
int main() {
int n;
cin >> n;
int *Pos = new int[n];
int *Array = new int[n + 1];
for (int i = 0; i < n; i++)
Array[i] = i + 1;
for (int i = 0; i < n; i++)//当前数组中值
cout << Array[i] << " ";
cout << endl;
int top = 0;
int m = n * (n + 1) / 4; //数组和的一半
int i, j;
int count = 0;//满足条件子集和的个数
i = 0;
memset(Pos, 0, sizeof(Pos));
while (i <= n && top >= 0) {
while (m > 0 && i < n) {
if (Array[i] <= m) {
Pos[top++] = i;
m -= Array[i];
}
i++;
}
if (m == 0) {
for (j = 0; j < top; j++) {
cout <<Array[Pos[j]]<<" ";
}
cout << endl;
count++;
}
i = Pos[--top];
if (top < 0)
break;
Pos[top] = 0;
m += Array[i];
i++;
}
cout<< count / 2 <<endl;
}