解法一
自己最开始写的dfs搜索,时间复杂度太高,为 O ( 3 n ) O(3^n) O(3n),只过了一半的数据。基本思想是枚举每一个砝码,每个砝码有三种状态,放左边,放右边,不放,放左边记为减法,放右边记为加法,依次枚举并对出现的所有结果用一个数组去重。
#include <iostream>
#include <queue>
#include <cmath>
#define ll long long
using namespace std;
const int maxn = 1e5 + 1;
int s[maxn] = { 0 };
int n, a[101] = { 0 };
ll ans = 0;
void dfs(int index, int sum) {
if (index > n + 1) {
return;
}
int k = fabs(sum);
if (k != 0 && s[k] == 0) {
s[k] = 1;
ans++;
}
//放左边
dfs(index + 1, sum - a[index]);
//放右边
dfs(index + 1, sum + a[index]);
//不放
dfs(index + 1, sum);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
dfs(1, 0);
cout << ans;
return 0;
}
解法二:
使用bfs遍历所有的结果的情况,虽然基本思想和dfs是相似的,但是时间复杂度却不一样,此种解法在最坏的情况下应该是每次出现的结果都没被访问过,时间复杂度应该是 O ( n 2 ) O(n^2) O(n2),我个人认为问题就出在了讨论每个砝码的三种状态这一步。
#include <iostream>
#include <queue>
#include <cmath>
#define ll long long
using namespace std;
const int maxn = 1e5 + 1;
int s[maxn] = { 0 };
int n, ans = 0;
queue<int> q;
int main()
{
cin >> n;
q.push(0);
for (int i = 1; i <= n; i++) {
int w; cin >> w;
//tmp_q可理解为一个备份
queue<int> tmp_q;
//q中存放当前已经遍历到的所有结果,将w与q中所有元素依次进行放左放右以及不放的操作
while (!q.empty()) {
int tmp = q.front();
q.pop();
//放右边
if (!s[tmp + w]) {
s[tmp + w] = 1;
ans++;
tmp_q.push(tmp + w);
}
//放左边
int abs = fabs(tmp - w);
if (!s[abs]) {
s[abs] = 1;
ans++;
tmp_q.push(abs);
}
//不放
tmp_q.push(tmp);
}
q = tmp_q;
}
//重量为0的情况不考虑,即两边砝码一样重时,虽然题目好像没说
if (s[0] == 1)
ans--;
cout << ans;
return 0;
}
解法三:
动态规划解法不多说,直接说一下状态转移方程:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
−
1
]
[
a
b
s
(
j
−
a
[
i
]
)
]
+
d
p
[
i
−
1
]
[
j
+
a
[
i
]
]
+
(
a
[
j
]
=
=
j
)
dp[i][j]=dp[i-1][j]+dp[i-1][abs(j-a[i])]+dp[i-1][j+a[i]]+(a[j]==j)
dp[i][j]=dp[i−1][j]+dp[i−1][abs(j−a[i])]+dp[i−1][j+a[i]]+(a[j]==j)
- 此处的 " + " "+" "+"为‘或’的意思
- d p [ i ] [ j ] dp[i][j] dp[i][j]的意义为使用前 i i i个砝码是否能够得到重量j
- 使得
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为真,下列条件有一条成立即可:
- d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]为真,原因是使用前 i − 1 i-1 i−1个砝码即可得到重量 j j j ,则使用前 i i i个当然也可以
- d p [ i − 1 ] [ a b s ( j − a [ i ] ) ] dp[i-1][abs(j-a[i])] dp[i−1][abs(j−a[i])]或 d p [ i − 1 ] [ j + a [ i ] ] dp[i-1][j+a[i]] dp[i−1][j+a[i]]为真,原因是此时第 i i i个砝码恰好可以与前 i − 1 i-1 i−1个砝码配合得到重量 j j j
- a [ i ] = = j a[i]==j a[i]==j为真,原因是即便前 i − 1 i-1 i−1个砝码都不能得到重量 j j j,但有第 i i i个也足矣
#include <iostream>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 1;
//动态规划解法
int n, cnt = 0, a[101], dp[101][maxn] = { 0 };
int main() {
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
}
dp[1][a[1]] = 1; cnt = 1;
for (int i = 2; i <= n; i++) {
//继承上一轮的结果
for (int j = 1; j <= sum; j++) {
dp[i][j] = dp[i - 1][j];
}
for (int j = 1; j <= sum; j++) {
if (dp[i][j] == 0) {
int abs = fabs(j - a[i]);
dp[i][j] = (a[i] == j) | dp[i - 1][abs] | dp[i - 1][j + a[i]];
if (dp[i][j] == 1)
cnt++;
}
}
}
cout << cnt;
return 0;
}
解法四:
纯数据结构解法,无算法
#include <iostream>
#include <set>
#include <vector>
#include <cmath>
using namespace std;
int n;
set<int> s;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int t; cin >> t;
vector<int> a(s.begin(), s.end());
for (int i = 0; i < a.size(); i++) {
s.insert(a[i] + t);
int abs = fabs(a[i] - t);
if (abs != 0) {
s.insert(abs);
}
}
s.insert(t);
}
cout << s.size();
return 0;
}