P4013 數字梯形問題
阿新 • • 發佈:2018-12-15
\(\color{#0066ff}{題目描述}\)
給定一個由 \(n\) 行數字組成的數字梯形如下圖所示。
梯形的第一行有 \(m\) 個數字。從梯形的頂部的 \(m\) 個數字開始,在每個數字處可以沿左下或右下方向移動,形成一條從梯形的頂至底的路徑。
分別遵守以下規則:
1.從梯形的頂至底的 \(m\) 條路徑互不相交;
2.從梯形的頂至底的 \(m\) 條路徑僅在數字結點處相交;
3.從梯形的頂至底的 \(m\) 條路徑允許在數字結點相交或邊相交。
\(\color{#0066ff}{輸入格式}\)
第 \(1\) 行中有 \(2\) 個正整數 \(m\) 和 \(n\),分別表示數字梯形的第一行有 \(m\)
第 \(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; }