A – 调整 Tweak
这道题我在考场上打了一个错解,还骗到了 30 分。
正常的思路是:在最短路图上找到几个边进行修改。然而,最优的解决方案并不一定出现在最短路图上的边上,也可以通过修改次优边进行解决。所以,这道题其实跟最短路没什么太大关系,即使后面会用到 Djisktra 来求最短路。
我们可以把一个\(G(V, E), |V| = n, |E| = m\)复制\(n\)份,然后对于第\(k\)份图中的有向边\((u_k, v_k, w_k)\),可以生成一个副本来连接下一层的目标点,也就是每一条边会派生出\((u_k, v_{k + 1}, 0)\),当最后求最短路时,经过这样的一条边可以被认为是修改一条边,且,这个边是单向向下的,所以不会出现同一层修改多次的情况。最后只需要求这张图的最短路,然后判定满足\(dist[n_k] \leq c\)的最小\(k\)就行了。
#define pr pair<int, int>
int n, m, c, dist[MAX_N * MAX_N], current, head[MAX_N * MAX_N];
} edges[1010 + MAX_N * MAX_N];
void addpath(int src, int dst, int weight)
edges[current].to = dst, edges[current].nxt = head[src];
edges[current].weight = weight, head[src] = current++;
memset(dist, 0x3f, sizeof(dist));
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (dist[edges[i].to] > dist[u] + edges[i].weight)
dist[edges[i].to] = dist[u] + edges[i].weight, q.push(make_pair(-dist[edges[i].to], edges[i].to));
memset(head, -1, sizeof(head));
scanf("%d%d%d", &n, &m, &c);
for (int i = 1; i <= m; i++)
scanf("%d%d%d", &u, &v, &w);
for (int layer = 0; layer < n; layer++)
addpath(layer * n + u, layer * n + v, w);
for (int layer = 0; layer < n - 1; layer++)
addpath(layer * n + u, (layer + 1) * n + v, 0);
for (int layer = 0; layer < n; layer++)
if (dist[layer * n + n] <= c)
// A.cpp
#include <bits/stdc++.h>
#define pr pair<int, int>
using namespace std;
const int MAX_N = 1100;
int n, m, c, dist[MAX_N * MAX_N], current, head[MAX_N * MAX_N];
bool vis[MAX_N * MAX_N];
struct edge
{
int to, nxt, weight;
} edges[1010 + MAX_N * MAX_N];
void addpath(int src, int dst, int weight)
{
edges[current].to = dst, edges[current].nxt = head[src];
edges[current].weight = weight, head[src] = current++;
}
void djisktra()
{
memset(dist, 0x3f, sizeof(dist));
priority_queue<pr> q;
q.push(make_pair(0, 1));
dist[1] = 0;
while (!q.empty())
{
int u = q.top().second;
q.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (dist[edges[i].to] > dist[u] + edges[i].weight)
dist[edges[i].to] = dist[u] + edges[i].weight, q.push(make_pair(-dist[edges[i].to], edges[i].to));
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d%d", &n, &m, &c);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
for (int layer = 0; layer < n; layer++)
addpath(layer * n + u, layer * n + v, w);
for (int layer = 0; layer < n - 1; layer++)
addpath(layer * n + u, (layer + 1) * n + v, 0);
}
djisktra();
for (int layer = 0; layer < n; layer++)
if (dist[layer * n + n] <= c)
{
printf("%d", layer);
return 0;
}
return 0;
}
// A.cpp
#include <bits/stdc++.h>
#define pr pair<int, int>
using namespace std;
const int MAX_N = 1100;
int n, m, c, dist[MAX_N * MAX_N], current, head[MAX_N * MAX_N];
bool vis[MAX_N * MAX_N];
struct edge
{
int to, nxt, weight;
} edges[1010 + MAX_N * MAX_N];
void addpath(int src, int dst, int weight)
{
edges[current].to = dst, edges[current].nxt = head[src];
edges[current].weight = weight, head[src] = current++;
}
void djisktra()
{
memset(dist, 0x3f, sizeof(dist));
priority_queue<pr> q;
q.push(make_pair(0, 1));
dist[1] = 0;
while (!q.empty())
{
int u = q.top().second;
q.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (dist[edges[i].to] > dist[u] + edges[i].weight)
dist[edges[i].to] = dist[u] + edges[i].weight, q.push(make_pair(-dist[edges[i].to], edges[i].to));
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d%d", &n, &m, &c);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
for (int layer = 0; layer < n; layer++)
addpath(layer * n + u, layer * n + v, w);
for (int layer = 0; layer < n - 1; layer++)
addpath(layer * n + u, (layer + 1) * n + v, 0);
}
djisktra();
for (int layer = 0; layer < n; layer++)
if (dist[layer * n + n] <= c)
{
printf("%d", layer);
return 0;
}
return 0;
}
B – 树 A
sb 题,树链剖分板子题,不讲。
#define rson ((p << 1) | 1)
#define mid ((l + r) >> 1)
int head[MAX_N], current, pt_weight[MAX_N], dfn[MAX_N], antidfn[MAX_N], m;
int top[MAX_N], son[MAX_N], siz[MAX_N], dep[MAX_N], fa[MAX_N], dfntot, n, opt;
void addpath(int src, int dst)
edges[current].to = dst, edges[current].nxt = head[src];
void build(int l, int r, int p)
return (void)(tree[p] = pt_weight[antidfn[l]]);
build(l, mid, lson), build(mid + 1, r, rson);
tree[p] = tree[lson] + tree[rson];
void update(int qx, int l, int r, int p, int val)
return (void)(tree[p] = val);
update(qx, l, mid, lson, val);
update(qx, mid + 1, r, rson, val);
tree[p] = tree[lson] + tree[rson];
int query(int ql, int qr, int l, int r, int p)
ans += query(ql, qr, l, mid, lson);
ans += query(ql, qr, mid + 1, r, rson);
if (dep[top[x]] > dep[top[y]])
ans += query(dfn[top[y]], dfn[y], 1, n, 1);
if (dep[top[x]] > dep[top[y]])
ans += query(dfn[x], dfn[y], 1, n, 1);
siz[u] = 1, dep[u] = dep[fa[u]] + 1;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u])
fa[edges[i].to] = u, dfs1(edges[i].to);
siz[u] += siz[edges[i].to];
if (siz[edges[i].to] > siz[son[u]])
void dfs2(int u, int org)
dfn[u] = ++dfntot, antidfn[dfntot] = u, top[u] = org;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u] && edges[i].to != son[u])
dfs2(edges[i].to, edges[i].to);
memset(head, -1, sizeof(head));
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
for (int i = 1; i <= n; i++)
scanf("%d", &pt_weight[i]);
scanf("%d%d%d", &opt, &A, &B);
update(dfn[A], 1, n, 1, B);
printf("%d\n", query(A, B));
// B.cpp
#include <bits/stdc++.h>
#define lson (p << 1)
#define rson ((p << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
const int MAX_N = 31000;
int head[MAX_N], current, pt_weight[MAX_N], dfn[MAX_N], antidfn[MAX_N], m;
int top[MAX_N], son[MAX_N], siz[MAX_N], dep[MAX_N], fa[MAX_N], dfntot, n, opt;
int tree[MAX_N << 2];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void build(int l, int r, int p)
{
if (l == r)
return (void)(tree[p] = pt_weight[antidfn[l]]);
build(l, mid, lson), build(mid + 1, r, rson);
tree[p] = tree[lson] + tree[rson];
}
void update(int qx, int l, int r, int p, int val)
{
if (l == r && qx == l)
return (void)(tree[p] = val);
if (qx <= mid)
update(qx, l, mid, lson, val);
else
update(qx, mid + 1, r, rson, val);
tree[p] = tree[lson] + tree[rson];
}
int query(int ql, int qr, int l, int r, int p)
{
if (ql <= l && r <= qr)
return tree[p];
int ans = 0;
if (ql <= mid)
ans += query(ql, qr, l, mid, lson);
if (mid < qr)
ans += query(ql, qr, mid + 1, r, rson);
return ans;
}
int query(int x, int y)
{
if (dep[top[x]] > dep[top[y]])
swap(x, y);
int ans = 0;
while (top[x] != top[y])
{
ans += query(dfn[top[y]], dfn[y], 1, n, 1);
y = fa[top[y]];
if (dep[top[x]] > dep[top[y]])
swap(x, y);
}
if (dep[x] > dep[y])
swap(x, y);
ans += query(dfn[x], dfn[y], 1, n, 1);
return ans;
}
void dfs1(int u)
{
siz[u] = 1, dep[u] = dep[fa[u]] + 1;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u])
{
fa[edges[i].to] = u, dfs1(edges[i].to);
siz[u] += siz[edges[i].to];
if (siz[edges[i].to] > siz[son[u]])
son[u] = edges[i].to;
}
}
void dfs2(int u, int org)
{
dfn[u] = ++dfntot, antidfn[dfntot] = u, top[u] = org;
if (son[u] == 0)
return;
dfs2(son[u], org);
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u] && edges[i].to != son[u])
dfs2(edges[i].to, edges[i].to);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
dfs1(1), dfs2(1, 1);
for (int i = 1; i <= n; i++)
scanf("%d", &pt_weight[i]);
build(1, n, 1);
while (m--)
{
int A, B;
scanf("%d%d%d", &opt, &A, &B);
if (opt == 1)
update(dfn[A], 1, n, 1, B);
else
printf("%d\n", query(A, B));
}
return 0;
}
// B.cpp
#include <bits/stdc++.h>
#define lson (p << 1)
#define rson ((p << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
const int MAX_N = 31000;
int head[MAX_N], current, pt_weight[MAX_N], dfn[MAX_N], antidfn[MAX_N], m;
int top[MAX_N], son[MAX_N], siz[MAX_N], dep[MAX_N], fa[MAX_N], dfntot, n, opt;
int tree[MAX_N << 2];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void build(int l, int r, int p)
{
if (l == r)
return (void)(tree[p] = pt_weight[antidfn[l]]);
build(l, mid, lson), build(mid + 1, r, rson);
tree[p] = tree[lson] + tree[rson];
}
void update(int qx, int l, int r, int p, int val)
{
if (l == r && qx == l)
return (void)(tree[p] = val);
if (qx <= mid)
update(qx, l, mid, lson, val);
else
update(qx, mid + 1, r, rson, val);
tree[p] = tree[lson] + tree[rson];
}
int query(int ql, int qr, int l, int r, int p)
{
if (ql <= l && r <= qr)
return tree[p];
int ans = 0;
if (ql <= mid)
ans += query(ql, qr, l, mid, lson);
if (mid < qr)
ans += query(ql, qr, mid + 1, r, rson);
return ans;
}
int query(int x, int y)
{
if (dep[top[x]] > dep[top[y]])
swap(x, y);
int ans = 0;
while (top[x] != top[y])
{
ans += query(dfn[top[y]], dfn[y], 1, n, 1);
y = fa[top[y]];
if (dep[top[x]] > dep[top[y]])
swap(x, y);
}
if (dep[x] > dep[y])
swap(x, y);
ans += query(dfn[x], dfn[y], 1, n, 1);
return ans;
}
void dfs1(int u)
{
siz[u] = 1, dep[u] = dep[fa[u]] + 1;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u])
{
fa[edges[i].to] = u, dfs1(edges[i].to);
siz[u] += siz[edges[i].to];
if (siz[edges[i].to] > siz[son[u]])
son[u] = edges[i].to;
}
}
void dfs2(int u, int org)
{
dfn[u] = ++dfntot, antidfn[dfntot] = u, top[u] = org;
if (son[u] == 0)
return;
dfs2(son[u], org);
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u] && edges[i].to != son[u])
dfs2(edges[i].to, edges[i].to);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
dfs1(1), dfs2(1, 1);
for (int i = 1; i <= n; i++)
scanf("%d", &pt_weight[i]);
build(1, n, 1);
while (m--)
{
int A, B;
scanf("%d%d%d", &opt, &A, &B);
if (opt == 1)
update(dfn[A], 1, n, 1, B);
else
printf("%d\n", query(A, B));
}
return 0;
}
C – 树 B
也是一道 SB 题,要不是我边的数组开小了,我考场上就能多 A 一题,那至于只有 50 分。
这道题很显然是一道树形 DP,考虑对要隔离的点进行标记,然后树形 DP 方程如下:
\[ \begin{cases} dp[u][0] = \sum_{v \in son(u)} min\{ dp[v][0], dp[v][1] \}[tag[v] = false] + dp[v][1][tag[v] = true] \\ dp[u][1] = e(e, fa[u]).weight \end{cases} \]
const int MAX_N = 1e6 + 200;
int n, m, head[MAX_N], current, fa[MAX_N], upward[MAX_N];
void addpath(int src, int dst, int weight)
edges[current].to = dst, edges[current].weight = weight;
edges[current].nxt = head[src], head[src] = current++;
dp[u][1] = edges[upward[u]].weight;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u])
fa[edges[i].to] = u, upward[edges[i].to] = i;
dp[u][0] += dp[edges[i].to][1];
dp[u][0] += min(dp[edges[i].to][0], dp[edges[i].to][1]);
memset(head, -1, sizeof(head));
for (int i = 1, u, v, c; i <= n - 1; i++)
scanf("%d%d%d", &u, &v, &c), addpath(u, v, c), addpath(v, u, c);
for (int i = 1, tmp; i <= m; i++)
scanf("%d", &tmp), tag[tmp] = true;
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200;
int n, m, head[MAX_N], current, fa[MAX_N], upward[MAX_N];
int dp[MAX_N][2];
bool tag[MAX_N];
struct edge
{
int to, nxt, weight;
} edges[MAX_N << 1];
void addpath(int src, int dst, int weight)
{
edges[current].to = dst, edges[current].weight = weight;
edges[current].nxt = head[src], head[src] = current++;
}
void preprocess(int u)
{
dp[u][1] = edges[upward[u]].weight;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u])
{
fa[edges[i].to] = u, upward[edges[i].to] = i;
preprocess(edges[i].to);
if (tag[edges[i].to])
dp[u][0] += dp[edges[i].to][1];
else
dp[u][0] += min(dp[edges[i].to][0], dp[edges[i].to][1]);
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1, u, v, c; i <= n - 1; i++)
scanf("%d%d%d", &u, &v, &c), addpath(u, v, c), addpath(v, u, c);
for (int i = 1, tmp; i <= m; i++)
scanf("%d", &tmp), tag[tmp] = true;
preprocess(1);
printf("%d", dp[1][0]);
return 0;
}
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200;
int n, m, head[MAX_N], current, fa[MAX_N], upward[MAX_N];
int dp[MAX_N][2];
bool tag[MAX_N];
struct edge
{
int to, nxt, weight;
} edges[MAX_N << 1];
void addpath(int src, int dst, int weight)
{
edges[current].to = dst, edges[current].weight = weight;
edges[current].nxt = head[src], head[src] = current++;
}
void preprocess(int u)
{
dp[u][1] = edges[upward[u]].weight;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa[u])
{
fa[edges[i].to] = u, upward[edges[i].to] = i;
preprocess(edges[i].to);
if (tag[edges[i].to])
dp[u][0] += dp[edges[i].to][1];
else
dp[u][0] += min(dp[edges[i].to][0], dp[edges[i].to][1]);
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1, u, v, c; i <= n - 1; i++)
scanf("%d%d%d", &u, &v, &c), addpath(u, v, c), addpath(v, u, c);
for (int i = 1, tmp; i <= m; i++)
scanf("%d", &tmp), tag[tmp] = true;
preprocess(1);
printf("%d", dp[1][0]);
return 0;
}
D – 跨时代
这道题真的毒瘤。
最开始的做法:考虑状态压缩,所有的木棍拼接成的长度的情况都存在桶中,然后枚举两边长度,枚举当前长度下的木棍状态,然后 AND 判断,也就是这样:
int n, li[MAX_N], mxlen, S = -0x3f3f3f3f, sizes[20 * 15];
int bucket[20 * 15][20 * 150];
for (int i = 1; i <= n; i++)
scanf("%d", &li[i]), mxlen += li[i];
for (int i = 0; i < (1 << n); i++)
for (int j = 1; j <= n; j++)
bucket[len][sizes[len]++] = i;
for (int lenA = mxlen; lenA >= 1; lenA--)
for (int lenB = mxlen; lenB >= 1; lenB--)
for (int x = 0; x < sizA; x++)
int statX = bucket[lenA][x];
for (int y = 0; y < sizA; y++)
int statY = bucket[lenA][y];
if ((statX & statY) != 0)
for (int z = 0; z < sizB; z++)
int statZ = bucket[lenB][z];
if((statX & statZ) != 0 || (statY & statZ) != 0)
for (int w = 0; w < sizB; w++)
int statW = bucket[lenB][w];
if ((statW & statX) != 0 || (statW & statY) != 0 || (statW & statZ) != 0)
// D.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 20;
int n, li[MAX_N], mxlen, S = -0x3f3f3f3f, sizes[20 * 15];
int bucket[20 * 15][20 * 150];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &li[i]), mxlen += li[i];
for (int i = 0; i < (1 << n); i++)
{
int len = 0;
for (int j = 1; j <= n; j++)
if (i & (1 << (j - 1)))
len += li[j];
bucket[len][sizes[len]++] = i;
}
for (int lenA = mxlen; lenA >= 1; lenA--)
{
int sizA = sizes[lenA];
if (sizA == 0)
continue;
if (lenA * mxlen <= S)
break;
for (int lenB = mxlen; lenB >= 1; lenB--)
{
int sizB = sizes[lenB];
if (sizB == 0)
continue;
if (lenA * lenB <= S)
break;
for (int x = 0; x < sizA; x++)
{
int statX = bucket[lenA][x];
for (int y = 0; y < sizA; y++)
{
int statY = bucket[lenA][y];
if ((statX & statY) != 0)
continue;
for (int z = 0; z < sizB; z++)
{
int statZ = bucket[lenB][z];
if((statX & statZ) != 0 || (statY & statZ) != 0)
continue;
for (int w = 0; w < sizB; w++)
{
int statW = bucket[lenB][w];
if ((statW & statX) != 0 || (statW & statY) != 0 || (statW & statZ) != 0)
continue;
S = max(S, lenA * lenB);
}
}
}
}
}
}
if (S < 0)
printf("No Solution");
else
printf("%d", S);
return 0;
}
// D.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 20;
int n, li[MAX_N], mxlen, S = -0x3f3f3f3f, sizes[20 * 15];
int bucket[20 * 15][20 * 150];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &li[i]), mxlen += li[i];
for (int i = 0; i < (1 << n); i++)
{
int len = 0;
for (int j = 1; j <= n; j++)
if (i & (1 << (j - 1)))
len += li[j];
bucket[len][sizes[len]++] = i;
}
for (int lenA = mxlen; lenA >= 1; lenA--)
{
int sizA = sizes[lenA];
if (sizA == 0)
continue;
if (lenA * mxlen <= S)
break;
for (int lenB = mxlen; lenB >= 1; lenB--)
{
int sizB = sizes[lenB];
if (sizB == 0)
continue;
if (lenA * lenB <= S)
break;
for (int x = 0; x < sizA; x++)
{
int statX = bucket[lenA][x];
for (int y = 0; y < sizA; y++)
{
int statY = bucket[lenA][y];
if ((statX & statY) != 0)
continue;
for (int z = 0; z < sizB; z++)
{
int statZ = bucket[lenB][z];
if((statX & statZ) != 0 || (statY & statZ) != 0)
continue;
for (int w = 0; w < sizB; w++)
{
int statW = bucket[lenB][w];
if ((statW & statX) != 0 || (statW & statY) != 0 || (statW & statZ) != 0)
continue;
S = max(S, lenA * lenB);
}
}
}
}
}
}
if (S < 0)
printf("No Solution");
else
printf("%d", S);
return 0;
}
然后,嗯,TLE 了。
最后我看别人的做法是这样的:考虑状态压缩一套边,也就是一对长或者是一对宽,状压的时候进行背包 DP 判断这一套边是否能被拆成两部分,如果能的话打一个标记。然后,进行搜索:DFS 的时候,时间复杂度是\(O(3^n)\)的,因为每一条边都有选或者是不选的状态,然后考虑分配长和宽就行了。
int n, ai[MAX_N], dp[MAX_N * 20], ans = -1;
void dfs(int dep, int statA, int lenA, int statB, int lenB)
if (flag[statA] && flag[statB])
ans = max(ans, (lenA * lenB) / 4);
dfs(dep + 1, statA, lenA, statB, lenB);
dfs(dep + 1, statA | (1 << dep), lenA + ai[dep + 1], statB, lenB);
dfs(dep + 1, statA, lenA, statB | (1 << dep), lenB + ai[dep + 1]);
for (int i = 1; i <= n; i++)
for (int stat = 1; stat < (1 << n); stat++)
for (int i = 1; i <= n; i++)
if ((stat >> (i - 1)) & 1)
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
if ((stat >> (i - 1)) & 1)
for (int k = sum; k >= ai[i]; k--)
// D.cpp
#include <bits/stdc++.h>
const int MAX_N = 17;
using namespace std;
int n, ai[MAX_N], dp[MAX_N * 20], ans = -1;
bool flag[(1 << MAX_N)];
void dfs(int dep, int statA, int lenA, int statB, int lenB)
{
if (dep == n)
{
if (flag[statA] && flag[statB])
ans = max(ans, (lenA * lenB) / 4);
return;
}
dfs(dep + 1, statA, lenA, statB, lenB);
dfs(dep + 1, statA | (1 << dep), lenA + ai[dep + 1], statB, lenB);
dfs(dep + 1, statA, lenA, statB | (1 << dep), lenB + ai[dep + 1]);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &ai[i]);
for (int stat = 1; stat < (1 << n); stat++)
{
int sum = 0;
for (int i = 1; i <= n; i++)
if ((stat >> (i - 1)) & 1)
sum += ai[i];
if (sum & 1)
continue;
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; i <= n; i++)
if ((stat >> (i - 1)) & 1)
for (int k = sum; k >= ai[i]; k--)
dp[k] |= dp[k - ai[i]];
if (dp[sum >> 1])
flag[stat] = true;
}
dfs(0, 0, 0, 0, 0);
if (ans > 0)
printf("%d", ans);
else
puts("No Solution");
return 0;
}
// D.cpp
#include <bits/stdc++.h>
const int MAX_N = 17;
using namespace std;
int n, ai[MAX_N], dp[MAX_N * 20], ans = -1;
bool flag[(1 << MAX_N)];
void dfs(int dep, int statA, int lenA, int statB, int lenB)
{
if (dep == n)
{
if (flag[statA] && flag[statB])
ans = max(ans, (lenA * lenB) / 4);
return;
}
dfs(dep + 1, statA, lenA, statB, lenB);
dfs(dep + 1, statA | (1 << dep), lenA + ai[dep + 1], statB, lenB);
dfs(dep + 1, statA, lenA, statB | (1 << dep), lenB + ai[dep + 1]);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &ai[i]);
for (int stat = 1; stat < (1 << n); stat++)
{
int sum = 0;
for (int i = 1; i <= n; i++)
if ((stat >> (i - 1)) & 1)
sum += ai[i];
if (sum & 1)
continue;
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; i <= n; i++)
if ((stat >> (i - 1)) & 1)
for (int k = sum; k >= ai[i]; k--)
dp[k] |= dp[k - ai[i]];
if (dp[sum >> 1])
flag[stat] = true;
}
dfs(0, 0, 0, 0, 0);
if (ans > 0)
printf("%d", ans);
else
puts("No Solution");
return 0;
}