蒜头君手上有一些小木棍,它们长短不一,蒜头君想用这些木棍拼出一个正方形,并且每根木棍都要用到。 例如,蒜头君手上有长度为1,2,3,3, 3的5根木棍,他可以让长度为1,2 的木棍组成一条边,另外三根分别组成3条边,拼成一个边长为3的正方形。蒜头君希望你提前告诉他能不能拼出来,免得白费功夫。
输入格式
首先输入一个整数n(4≤n≤20),表示木棍数量,接下来输入n根木棍的长度p
i(1≤pi≤10000)。
输出格式
如果蒜头君能拼出正方形,输出"Yes",否则输出"No"。
样例输入1
4
1 1 1 1
样例输出1
Yes
样例输入2
5
10 20 30 40 50
样例输出2
No
#include <bits/stdc++.h>
using namespace std;
int n, p[20], i0 = 0, judge = 0, s0, k = 3;
void dfs(int s) {
if (s == 0) {
judge = 1;
} else if (s > 0) {
for (int i = i0; i < n; i++) {
if (p[i] != 0) {
int p0 = 0, i1 = i0;
i0 = i + 1;
p0 = p[i];
p[i] = 0;
dfs(s - p0);
if (judge == 0) {
p[i] = p0;
i0 = i1;
} else break;
}
}
}
}
int solve(int s) {
while (k--) {
judge = 0;
i0 = 0;
dfs(s);
if (judge == 0)
return 0;
}
return 1;
}
int main() {
int i, ss = 0;
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d", &p[i]);
ss += p[i];
}
if (ss % 4 != 0)
printf("No");
else {
s0 = ss / 4;
if (solve(s0))
printf("Yes");
else
printf("No");
}
return 0;
}
只通过部分样例。
例如
8
1 3 2 6 4 4 3 5
首先正方形边长为7
则1,3,3一组;2,5一组剩下6,4,4不能组成两个长为7的边,程序输出“No”。
但是当3,4一组;3,4一组;6,1一组;2,5一组时满足条件。
为了防止出现这种情况,我分别从正反两个方向遍历
#include <bits/stdc++.h>
using namespace std;
int n, p[20], q[20], i0 = 0, judge = 0, s0, k = 3;
void dfs(int s) {
if (s == 0) {
judge = 1;
} else if (s > 0) {
for (int i = i0; i < n; i++) {
if (p[i] != 0) {
int p0 = 0, i1 = i0;
i0 = i + 1;
p0 = p[i];
p[i] = 0;
dfs(s - p0);
if (judge == 0) {
p[i] = p0;
i0 = i1;
} else break;
}
}
}
}
void dfs1(int s) {
if (s == 0) {
judge = 1;
} else if (s > 0) {
for (int i = i0; i >= 0; i--) {
if (q[i] != 0) {
int p0 = 0, i1 = i0;
i0 = i - 1;
p0 = q[i];
q[i] = 0;
dfs1(s - p0);
if (judge == 0) {
q[i] = p0;
i0 = i1;
} else break;
}
}
}
}
int solve(int s) {
while (k--) {
judge = 0;
i0 = 0;
dfs(s);
if (judge == 0)
return 0;
}
return 1;
}
int solve1(int s) {
k = 3;
while (k--) {
judge = 0;
i0 = n - 1;
dfs1(s);
if (judge == 0)
return 0;
}
return 1;
}
int main() {
int i, ss = 0;
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d", &p[i]);
ss += p[i];
}
for (i = 0; i < n; i++)
q[i] = p[i];
if (ss % 4 != 0)
printf("No");
else {
s0 = ss / 4;
int t1 = solve(s0), t2 = solve1(s0);
if (t1 || t2)
printf("Yes");
else
printf("No");
}
return 0;
}
结果10组数据通过9组,比上一版本好了些
20
1110 869 427 399 1058 878 658 551 528 661 67 559 1188 555 167 720 288 210 937 242
上面这个样例没有通过。
查看了别人的代码发现要排序,于是我尝试着先排序后再用上面的程序正反遍历,结果居然AC了。
仔细想一想发现,如果可以组成正方形的数据没有找到方案基本上是最后剩下较大的数据没有的数据匹配成边。因为较大的数据一般要和较小的数据组合成边,但是几个较小的数据或者中等大小的数据和几个较小的数据也能组合成边,当情况为后两者时较大的数据会剩下,无法组合成边,得到错误结果。因此类似于贪心算法,先将较大的数据与其它数据组合成边再匹配剩下的边更为合理。然而,当各个数据相差不大时,这种原则并不一定可靠,因此再优先匹配较小的,两种情况和取得到最终结果。
最终版本:
#include <bits/stdc++.h>
using namespace std;
int n, p[20], q[20], i0 = 0, judge = 0, s0, k = 3;
void dfs(int s) {
if (s == 0) {
judge = 1;
} else if (s > 0) {
for (int i = i0; i < n; i++) {
if (p[i] != 0) {
int p0 = 0, i1 = i0;
i0 = i + 1;
p0 = p[i];
p[i] = 0;
dfs(s - p0);
if (judge == 0) {
p[i] = p0;
i0 = i1;
} else break;
}
}
}
}
void dfs1(int s) {
if (s == 0) {
judge = 1;
} else if (s > 0) {
for (int i = i0; i >= 0; i--) {
if (q[i] != 0) {
int p0 = 0, i1 = i0;
i0 = i - 1;
p0 = q[i];
q[i] = 0;
dfs1(s - p0);
if (judge == 0) {
q[i] = p0;
i0 = i1;
} else break;
}
}
}
}
int solve(int s) {
while (k--) {
judge = 0;
i0 = 0;
dfs(s);
if (judge == 0)
return 0;
}
return 1;
}
int solve1(int s) {
k = 3;
while (k--) {
judge = 0;
i0 = n - 1;
dfs1(s);
if (judge == 0)
return 0;
}
return 1;
}
int main() {
int i, ss = 0;
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d", &p[i]);
ss += p[i];
}
sort(p,p+n);
for (i = 0; i < n; i++)
q[i] = p[i];
if (ss % 4 != 0)
printf("No");
else {
s0 = ss / 4;
int t1 = solve(s0), t2 = solve1(s0);
if (t1 || t2)
printf("Yes");
else
printf("No");
}
return 0;
}