Loading [MathJax]/extensions/tex2jax.js

「Fortuna OJ」Jul 6th – Group B 解题报告

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\)就行了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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 题,树链剖分板子题,不讲。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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} \]

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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 判断,也就是这样:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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)\)的,因为每一条边都有选或者是不选的状态,然后考虑分配长和宽就行了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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;
}

Leave a Reply

Your email address will not be published. Required fields are marked *