起初看到题目,想了许久仍是没有思路,看了一位大佬用dp做的思路,深受启发
LanQiao-ALGO-1004 无聊的逗 (动态规划: 0-1背包问题) -- (LeetCode 416. 分割等和子集 变种题)_Mr.xiao的博客-CSDN博客
但是我只看懂了dp部分,其他还是略显复杂没有细究。
我自己在不断地debug中找到了更为简便的方法。
思路如下:
可以设背包容量为sum/2,dp[i][j]就是把1~i个重量与长度相等的棍子放进容量为j的背包里
一、若装进去的最大长度等于sum/2(意思是恰好有m根棍子能拼接成总长度的一半),则剩下的n-m根棍子也能拼接sum/2的长度。
二、实现一的前提是,棍子的总长度是偶数,这样才能分出两条拼接长度相等的棍子
比如 1 2 3 1,总长度是7,sum/2是3,我们需要拿掉一个1,总长度变成6,剩下的1 2 3就可以分为 1 2 和3,所以最长长度为3。
那么我们怎么知道拿出哪一根棍子才好呢?
1.首先我们得拿出一根棍子,使总长度为偶数。
2.棍子被拿出一条后总长度尽量大
总结:从最小的棍子开始判断,若拿出该棍子后总长度为偶数即符合条件!
代码如下
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main() {
int n, sum = 0;
cin >> n;
int A[n + 1] = {0};
for (int i = 1; i <= n; i++) {
cin >> A[i];
sum += A[i];
}
sort(A, A + n + 1);//需对长度进行排序
if (sum % 2) {//总长度为奇数的情况
for (int i = 1; i <= n; i++) {
if ((sum - A[i]) % 2 == 0) {//长度最小并使总长度为偶数的长度
sum -= A[i];
for (int j = i; j <= n; j++)
A[j] = A[j + 1];
n--;
break;
}
}
}
int dp[n + 1][sum / 2 + 1];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum / 2; j++) {
if (A[i] > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j],A[i] + dp[i - 1][j - A[i]])
}
}
}
if (dp[n][sum / 2] == sum / 2) {//最后判断一下有没有m根棍子能凑成sum/2的长度
cout << dp[n][sum / 2];
} else {
cout << 0;
}
}