Skip to content

Commit 2ef9b62

Browse files
committed
sprint 7
1 parent d06bfff commit 2ef9b62

2 files changed

Lines changed: 410 additions & 0 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import java.io.EOFException;
2+
import java.io.IOException;
3+
import java.io.InputStream;
4+
import java.io.OutputStream;
5+
6+
/*
7+
Принцип работы алгоритма:
8+
Нужно понять, можно ли выбрать часть партий так, чтобы сумма очков в них была равна половине общей суммы.
9+
Если общая сумма нечётная, две равные части получить нельзя.
10+
Иначе решаем задачу о достижимой сумме: храним битовую динамику reachable, где бит s равен 1,
11+
если некоторым набором уже рассмотренных партий можно набрать сумму s. Изначально достижима сумма 0.
12+
Для очередного значения x обновляем reachable операцией reachable |= reachable << x.
13+
После обработки всех значений проверяем достижимость суммы total / 2.
14+
15+
Доказательство корректности:
16+
После обработки первых i партий инвариант такой: бит s в reachable установлен тогда и только тогда,
17+
когда из этих i партий можно выбрать подмножество с суммой s.
18+
База: до обработки партий достижима только сумма 0, это пустое подмножество.
19+
Переход: для партии с очками x каждая старая достижимая сумма s остаётся достижимой, если не брать эту партию,
20+
а сумма s + x становится достижимой, если взять её. Именно это делает reachable |= reachable << x.
21+
По индукции после всех партий reachable содержит ровно все суммы, которые можно набрать подмножеством партий.
22+
Разбиение на две равные части существует тогда и только тогда, когда общая сумма чётная и достижима сумма total / 2:
23+
выбранное подмножество даёт первую часть, все остальные партии — вторую с такой же суммой.
24+
25+
Сложность:
26+
Пусть S — общая сумма очков, S <= 300 * 300 = 90000.
27+
Временная сложность — O(n * S / 64), потому что суммы хранятся битами в long-блоках.
28+
Пространственная сложность — O(S / 64).
29+
*/
30+
public class EqualSums {
31+
32+
static boolean solve(int[] points) {
33+
int total = 0;
34+
for (int point : points) {
35+
total += point;
36+
}
37+
38+
if ((total & 1) == 1) {
39+
return false;
40+
}
41+
42+
int target = total / 2;
43+
long[] reachable = new long[(target >> 6) + 1];
44+
reachable[0] = 1L;
45+
46+
for (int point : points) {
47+
if (point <= 0 || point > target) {
48+
continue;
49+
}
50+
51+
addPoint(reachable, point);
52+
53+
if (isReachable(reachable, target)) {
54+
return true;
55+
}
56+
}
57+
58+
return isReachable(reachable, target);
59+
}
60+
61+
private static void addPoint(long[] reachable, int point) {
62+
int wordShift = point >> 6;
63+
int bitShift = point & 63;
64+
65+
for (int word = reachable.length - 1; word >= wordShift; word--) {
66+
long shifted = reachable[word - wordShift] << bitShift;
67+
68+
if (bitShift != 0 && word - wordShift - 1 >= 0) {
69+
shifted |= reachable[word - wordShift - 1] >>> (64 - bitShift);
70+
}
71+
72+
reachable[word] |= shifted;
73+
}
74+
}
75+
76+
private static boolean isReachable(long[] reachable, int sum) {
77+
return (reachable[sum >> 6] & (1L << (sum & 63))) != 0;
78+
}
79+
80+
// -------------------- FAST INPUT --------------------
81+
static final class FastIn {
82+
private final InputStream in;
83+
private final byte[] buf = new byte[1 << 16];
84+
private int ptr = 0;
85+
private int len = 0;
86+
87+
FastIn(InputStream in) {
88+
this.in = in;
89+
}
90+
91+
private int read() throws IOException {
92+
if (ptr >= len) {
93+
len = in.read(buf);
94+
ptr = 0;
95+
96+
if (len <= 0) {
97+
return -1;
98+
}
99+
}
100+
101+
return buf[ptr++];
102+
}
103+
104+
int nextInt() throws IOException {
105+
int c;
106+
107+
do {
108+
c = read();
109+
110+
if (c == -1) {
111+
throw new EOFException("Unexpected EOF");
112+
}
113+
} while (c <= ' ');
114+
115+
int sign = 1;
116+
117+
if (c == '-') {
118+
sign = -1;
119+
c = read();
120+
}
121+
122+
int val = 0;
123+
124+
while (c > ' ') {
125+
val = val * 10 + (c - '0');
126+
c = read();
127+
}
128+
129+
return val * sign;
130+
}
131+
}
132+
133+
// -------------------- FAST OUTPUT --------------------
134+
static final class FastOut {
135+
private final OutputStream out;
136+
private final byte[] buf = new byte[1 << 16];
137+
private int p = 0;
138+
139+
FastOut(OutputStream out) {
140+
this.out = out;
141+
}
142+
143+
void writeByte(int b) throws IOException {
144+
if (p == buf.length) {
145+
flush();
146+
}
147+
148+
buf[p++] = (byte) b;
149+
}
150+
151+
void writeAscii(String s) throws IOException {
152+
for (int i = 0; i < s.length(); i++) {
153+
writeByte(s.charAt(i));
154+
}
155+
}
156+
157+
void flush() throws IOException {
158+
out.write(buf, 0, p);
159+
p = 0;
160+
}
161+
}
162+
163+
private static void run() throws Exception {
164+
FastIn in = new FastIn(System.in);
165+
FastOut out = new FastOut(System.out);
166+
167+
int n = in.nextInt();
168+
int[] points = new int[n];
169+
170+
for (int i = 0; i < n; i++) {
171+
points[i] = in.nextInt();
172+
}
173+
174+
out.writeAscii(solve(points) ? "True" : "False");
175+
out.writeByte('\n');
176+
out.flush();
177+
}
178+
179+
private static void test() {
180+
assertEq(true, solve(new int[]{1, 5, 7, 1}));
181+
assertEq(false, solve(new int[]{2, 10, 9}));
182+
assertEq(true, solve(new int[]{}));
183+
assertEq(true, solve(new int[]{0, 0, 0}));
184+
assertEq(false, solve(new int[]{1}));
185+
assertEq(true, solve(new int[]{1, 1}));
186+
assertEq(true, solve(new int[]{100, 200, 300}));
187+
assertEq(false, solve(new int[]{100, 200, 301}));
188+
189+
System.out.println("Test OK");
190+
}
191+
192+
static void assertEq(boolean exp, boolean act) {
193+
if (exp != act) {
194+
throw new AssertionError("Expected=" + exp + ", actual=" + act);
195+
}
196+
}
197+
198+
public static void main(String[] args) throws Exception {
199+
if (System.getProperty("os.name").startsWith("Windows")) {
200+
test();
201+
} else {
202+
run();
203+
}
204+
}
205+
}

0 commit comments

Comments
 (0)