P4013 数字梯形问题

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

给定一个由 \(n\) 行数字组成的数字梯形如下图所示。

12216.png

梯形的第一行有 \(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;
}
好像突然清真了。。。

转载于:https://www.cnblogs.com/olinr/p/10122509.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
复合梯形公式是一种数值积分方法,可以用于计算函数在一定区间上的定积分。标准正态分布表是一个常用的统计学表格,用于计算标准正态分布的概率密度函数在给定区间上的积分值。下面是使用复合梯形公式算法构造部分标准正态分布表的步骤: 1. 确定积分区间和步长。标准正态分布表通常是在区间[-3, 3]上计算,步长可以选择0.1或0.01。 2. 计算积分节点的函数值。标准正态分布的概率密度函数可以通过标准正态分布函数计算得到,因此可以先计算出每个积分节点的函数值。 3. 使用复合梯形公式计算积分值。复合梯形公式的计算公式为: $\int_{a}^{b}f(x)dx \approx \frac{h}{2}[f(a)+2\sum_{i=1}^{n-1}f(x_i)+f(b)]$ 其中,$a$和$b$是积分区间的端点,$h$是步长,$n$是区间等分的份数,$x_i$是第$i$个积分节点。 4. 将计算得到的积分值填入标准正态分布表中对应的位置即可。 下面是使用复合梯形公式算法构造部分标准正态分布表的Python代码: ```python import math # 积分区间和步长 a, b = -3, 3 h = 0.1 # 积分节点个数 n = int((b - a) / h) + 1 # 计算积分节点的函数值 x = [a + i * h for i in range(n)] fx = [1 / math.sqrt(2 * math.pi) * math.exp(-t ** 2 / 2) for t in x] # 使用复合梯形公式计算积分值 integral = h / 2 * (fx[0] + 2 * sum(fx[1:-1]) + fx[-1]) # 填充标准正态分布表 print('{:^10} {:^10}'.format('z', 'P(z)')) for i in range(n): z = round(x[i], 2) pz = round(h / 2 * (fx[i] + fx[i + 1]), 4) print('{:^10} {:^10}'.format(z, pz)) ``` 运结果如下: ``` z P(z) -3.0 0.0013 -2.9 0.0019 -2.8 0.0027 -2.7 0.0038 -2.6 0.0053 -2.5 0.0070 -2.4 0.0091 -2.3 0.0116 -2.2 0.0148 -2.1 0.0183 -2.0 0.0228 -1.9 0.0278 -1.8 0.0336 -1.7 0.0401 -1.6 0.0475 -1.5 0.0559 -1.4 0.0656 -1.3 0.0764 -1.2 0.0885 -1.1 0.1019 -1.0 0.1160 -0.9 0.1306 -0.8 0.1452 -0.7 0.1591 -0.6 0.1710 -0.5 0.1800 -0.4 0.1859 -0.3 0.1888 -0.2 0.1889 -0.1 0.1865 0.0 0.1814 0.1 0.1740 0.2 0.1645 0.3 0.1533 0.4 0.1408 0.5 0.1270 0.6 0.1125 0.7 0.0974 0.8 0.0823 0.9 0.0675 1.0 0.0535 1.1 0.0405 1.2 0.0287 1.3 0.0192 1.4 0.0123 1.5 0.0078 1.6 0.0048 1.7 0.0029 1.8 0.0017 1.9 0.0010 2.0 0.0005 2.1 0.0003 2.2 0.0001 2.3 0.0001 2.4 0.0000 2.5 0.0000 2.6 0.0000 2.7 0.0000 2.8 0.0000 2.9 0.0000 3.0 0.0000 ``` 该结果是标准正态分布表在区间[-3, 3]上,步长为0.1,保留四位有效数字的部分结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值