P1466 [USACO2.2] 集合 Subset Sums

题目描述

对于从 1n 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。举个例子,如果 n=3,对于 {1,2,3} 能划分成两个子集合,每个子集合的所有数字和是相等的:

{3}{1,2} 是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)
如果 n=7,有四种方法能划分集合 {1,2,3,4,5,6,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. 如果用next_permutation找组合会TLE,所以用DP
  2. 1<=n<=39 打表出省一 (doge
  3. 如果1 ~ n 的和是奇数,答案肯定是0
  4. 定义状态:f[s]=将序列分为两部分,每部分的和为s,(计入重复,所以最后cout要/2)
  5. 状态转移方程: if (j >= i) f[j] += f[j - i];
  6. 初始化:f[0] = 1;

带马(暴力+正解+打表)

暴力

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[40];
int c[40];
signed main() {
cin >> n;
if ((n * (n + 1) / 2) & 1) {
cout << 0 << ",";
return 0;
}
fill(a + 1, a + n + 1, 0);
iota(a + 1, a + n + 1, 1);
int ans = 0;
for (int l = 0; l <= n; l++) {
fill(c + 1, c + n + 1, 0);
int pos = n;
for (int i = 0; i < l; i++) {
c[pos--] = 1;
}
do {
int s1 = 0, s2 = 0;
for (int i = 1; i <= n; i++) {
if (c[i]) {
s1 += a[i];
} else {
s2 += a[i];
}
}
if (s1 == s2) {
ans++;
}
} while (next_permutation(c + 1, c + n + 1));
}
cout << ans / 2 << ",";
return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int f[1000];
signed main() {
cin >> n;
int s = n*(n + 1) / 2;
if (s & 1) {
cout << 0;
return 0;
}
s /= 2;
f[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = s; j >= 1; j--) {
if (j >= i) f[j] += f[j - i];
}
}
cout << f[s] / 2;
return 0;
}

打表

#include<bits/stdc++.h>
#define int long long
using namespace std;
int k[] = {0, 0, 0, 1, 1, 0, 0, 4, 7, 0, 0, 35, 62, 0, 0, 361, 657, 0, 0, 4110, 7636, 0, 0, 49910, 93846, 0, 0, 632602, 1199892, 0, 0, 8273610, 15796439, 0, 0, 110826888, 212681976, 0, 0, 1512776590};
signed main() {
int n;
cin >> n;
cout << k[n];
return 0;
}

posted on   可爱楷玩算法  阅读(18)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示