1. 程式人生 > >P4013 數字梯形問題

P4013 數字梯形問題

\(\color{#0066ff}{題目描述}\)

給定一個由 \(n\) 行數字組成的數字梯形如下圖所示。

梯形的第一行有 \(m\) 個數字。從梯形的頂部的 \(m\) 個數字開始,在每個數字處可以沿左下或右下方向移動,形成一條從梯形的頂至底的路徑。

分別遵守以下規則:

1.從梯形的頂至底的 \(m\) 條路徑互不相交;

2.從梯形的頂至底的 \(m\) 條路徑僅在數字結點處相交;

3.從梯形的頂至底的 \(m\) 條路徑允許在數字結點相交或邊相交。

\(\color{#0066ff}{輸入格式}\)

\(1\) 行中有 \(2\) 個正整數 \(m\)\(n\),分別表示數字梯形的第一行有 \(m\)

個數字,共有 \(n\) 行。接下來的 \(n\) 行是數字梯形中各行的數字。

\(1\) 行有 \(m\) 個數字,第 \(2\) 行有 \(m+1\) 個數字,以此類推。

\(\color{#0066ff}{輸出格式}\)

將按照規則 \(1\),規則 \(2\),和規則 \(3\) 計算出的最大數字總和並輸出,每行一個最大總和。

\(\color{#0066ff}{輸入樣例}\)

2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1

\(\color{#0066ff}{輸出樣例}\)

66
75
77

\(\color{#0066ff}{資料範圍與提示}\)

\(1\leq m,n\leq 20\)

\(\color{#0066ff}{題解}\)

把點權轉成邊權,每個位置拆點

第一問,所有容量均為1,這樣都不會重複

x流到y',讓y'連向y,這樣進行下次流動

每一個點向下一層到的點連邊,最後一層向終點連邊

第二問,因為點可以重複,要考慮終點!!!

把x'到x的邊改成inf,這樣每個點可以接受來自上面多個點的流

還要把連向t的流改成inf,終點位置可能重合!

第三問,中間的所有邊改成inf就行,因為終究還是m條路,所以起點的連邊還是1

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define _ 0
#define LL long long
inline LL in() {
    LL x = 0, f = 1; char ch;
    while (!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    while (isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
    return x * f;
}
const int maxn = 105000;
struct node {
    int to, dis, can;
    node *nxt, *rev;
    node(int to = 0, int dis = 0, int can = 0, node *nxt = NULL) : to(to), dis(dis), can(can), nxt(nxt) {}
};
const int inf = 0x7fffffff;
int n, m, s, t, cnt;
std::queue<int> q;
typedef node *nod;
nod head[maxn], road[maxn];
bool vis[maxn];
int dis[maxn], change[maxn], mp[505][550], id[550][505];
inline void add(int from, int to, int can, int dis) {
    nod o = new node(to, dis, can, head[from]);
    head[from] = o;
}
inline void link(int from, int to, int can, int dis) {
    add(from, to, can, dis);
    add(to, from, 0, -dis);
    head[from]->rev = head[to];
    head[to]->rev = head[from];
}
inline bool spfa() {
    for (int i = s; i <= t; i++) dis[i] = -inf, change[i] = inf;
    dis[s] = 0;
    q.push(s);
    while (!q.empty()) {
        int tp = q.front();
        q.pop();
        vis[tp] = false;
        for (nod i = head[tp]; i; i = i->nxt) {
            if (dis[i->to] < dis[tp] + i->dis && i->can > 0) {
                dis[i->to] = dis[tp] + i->dis;
                change[i->to] = std::min(change[tp], i->can);
                road[i->to] = i->rev;
                if (!vis[i->to])
                    vis[i->to] = true, q.push(i->to);
            }
        }
    }
    return change[t] != inf;
}
inline int mcmf() {
    int flow = 0, cost = 0;
    while (spfa()) {
        flow += change[t];
        cost += change[t] * dis[t];
        for (int i = t; i != s; i = road[i]->to) {
            road[i]->can += change[t];
            road[i]->rev->can -= change[t];
        }
    }
    return cost;
}
inline void partone() {
    for (int i = 1; i <= m; i++) link(s, id[1][i], 1, mp[1][i]);
    for (int i = 1; i <= m + n - 1; i++) link(id[n][i] + cnt, t, 1, 0);
    for (int i = 1; i <= cnt; i++) link(i + cnt, i, 1, 0);
    for (int i = 1; i <= n - 1; i++)
        for (int j = 1; j <= m + i - 1; j++)
            link(id[i][j], id[i + 1][j] + cnt, 1, mp[i + 1][j]),
                link(id[i][j], id[i + 1][j + 1] + cnt, 1, mp[i + 1][j + 1]);
    printf("%d\n", mcmf());
}
inline void parttwo() {
    for (int i = s; i <= t; i++) head[i] = NULL;
    for (int i = 1; i <= m; i++) link(s, id[1][i], 1, mp[1][i]);
    for (int i = 1; i <= m + n - 1; i++) link(id[n][i] + cnt, t, inf, 0);
    for (int i = 1; i <= cnt; i++) link(i + cnt, i, inf, 0);
    for (int i = 1; i <= n - 1; i++)
        for (int j = 1; j <= m + i - 1; j++)
            link(id[i][j], id[i + 1][j] + cnt, 1, mp[i + 1][j]),
                link(id[i][j], id[i + 1][j + 1] + cnt, 1, mp[i + 1][j + 1]);
    printf("%d\n", mcmf());
}
inline void partthree() {
    for (int i = s; i <= t; i++) head[i] = NULL;
    for (int i = 1; i <= m; i++) link(s, id[1][i], 1, mp[1][i]);
    for (int i = 1; i <= m + n - 1; i++) link(id[n][i] + cnt, t, inf, 0);
    for (int i = 1; i <= cnt; i++) link(i + cnt, i, inf, 0);
    for (int i = 1; i <= n - 1; i++)
        for (int j = 1; j <= m + i - 1; j++)
            link(id[i][j], id[i + 1][j] + cnt, inf, mp[i + 1][j]),
                link(id[i][j], id[i + 1][j + 1] + cnt, inf, mp[i + 1][j + 1]);
    printf("%d\n", mcmf());
}
int main() {
    m = in(), n = in();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m + i - 1; j++) mp[i][j] = in(), id[i][j] = ++cnt;
    s = 0, t = (cnt << 1) + 1;
    partone(), parttwo(), partthree();
    return 0;
}

好像突然清真了。。。