题解:AcWing 1365 子集的和
【题目来源】
AcWing:1365. 子集的和 - AcWing题库
【题目描述】
对于很多由 \(1∼N\) 构成的连续整数集合,我们都可以将其划分为两个子集,并使得两个子集的和相等。
例如,当 \(N=3\) 时,我们可以将集合 \(\{1,2,3\}\) 划分为子集 \(\{1,2\}\) 和 \(\{3\}\),这也是唯一的一种满足条件的划分方式。
当 \(N=7\) 时,共有四种满足条件的划分方式,如下所示
\(\{1,6,7\}\) 和 \(\{2,3,4,5\}\)
\(\{2,5,7\}\) 和 \(\{1,3,4,6\}\)
\(\{3,4,7\}\) 和 \(\{1,2,5,6\}\)
\(\{1,2,4,7\}\) 和 \(\{3,5,6\}\)
现在,给定 \(N\),请你计算将 \(1∼N\) 构成的连续整数集合划分为和相等的两个子集,共有多少种划分方式。
将一种划分方式的某个子集内部的元素之间进行顺序调整仍看作是同一种划分方式。
【输入】
共一行包含整数 \(N\)。
【输出】
输出一个整数,表示划分方案数。
如果无法划分,则输出 \(0\)。
【输入样例】
7
【输出样例】
4
【算法标签】
《AcWing 1365 子集的和》 #DP# #背包问题# #二进制# #DFS# #BFS# #哈希表#
【代码详解】
#include<iostream>
#include<cstdio>
using namespace std;
#define int long long
int n, a[45], x, dp[45][1605], maxn; // n: 数字个数,a: 数字数组,x: 总和,dp: 动态规划数组
signed main()
{
cin >> n; // 读入n
for (int i = 1; i <= n; i++)
{
a[i] = i; // 初始化数组为1,2,3,...,n
x += i; // 计算总和
}
// cout<<x<<endl; // 调试输出
// 特判:如果总和是奇数,无法分成两个相等的子集
if (x % 2)
{
cout << 0;
return 0;
}
dp[0][0] = 1; // 用0个数字凑出和为0的方法数为1
// 动态规划:01背包问题
// dp[i][j]表示用前i个数字凑出和为j的方法数
for (int i = 1; i <= n; i++) // 遍历每个数字
{
for (int j = 0; j <= x; j++) // 遍历所有可能的和
{
dp[i][j] = dp[i - 1][j]; // 不使用第i个数字
if (j >= a[i]) // 如果当前和j大于等于第i个数字
{
dp[i][j] += dp[i - 1][j - a[i]]; // 使用第i个数字
}
}
}
// 输出结果为凑出x/2的方法数除以2
// 因为每个划分方案会被计算两次(两个对称的子集)
cout << dp[n][x / 2] / 2;
return 0;
}
【运行结果】
7
4
浙公网安备 33010602011771号