codevs4438 YJQ Runs Upstairs

Description

学校科技楼一共有 \(N\) 层,而神犇YJQ每天都在科技楼 \(N\) 楼的机房写代码。这天,他准备从科技楼 \(1\) 楼爬到 \(N\) 楼。有个 \(M\) 连接不同楼层的楼梯,爬每个楼梯需要一定的体力值。楼梯一定是从低处通往高处的。(但是由于楼房的设计比较奇怪,第 \(i\) 楼并不一定在第 \(i−1\) 楼上面,也就是说给出的边不保证 \(x<y\) ,但保证图为DAG,请自行处理楼层之间的高度关系)。为了省时间,YJQ一定只会上楼梯而不会下楼梯,即楼梯间不会形成环路。而且出于人性化考虑,不管YJQ选择什么路线上楼,他爬的楼梯数量一定小于 \(20\) 。为了使体力消耗尽量平稳,YJQ需要选择一条“每个楼梯消耗体力值的方差最小”的路径上楼。请帮助YJQ计算出这个最小方差。

Input Description

第一行包含 \(2\) 个整数 \(N,M\) 表示科技楼楼层数和楼梯数; 接下来 \(M\) 行,每行 \(3\) 个数, \(x,y,z\) 表示存在一条由 \(x\) 层通往平台 \(y\) 层的楼梯,爬这个楼梯需要消耗 \(z\) 的体力值。

Output Description

一行 \(1\) 个实数,表示最小方差,精确到小数点后 \(4\) 位。

Sample Input

4 4 1 2 1 2 4 3 1 3 2 3 4 3

Sample Output

0.2500

Data Size & Hint

对于 \(30\%\) 的数据,\(N≤10,M≤20\);

另有 \(20\%\) 的数据 \(N≤35,M≤220,Z\in[0,1]\);

对于 \(100\%\) 的数据 \(2≤N≤50,M≤300,0≤Z≤50\) 保证至少存在一条由 \(1\)\(N\) 的路径。

Solution

方差 \(s^2\) 满足 \[ s^2 = \frac{1}{n}\times\sum^n_{i=1}(x_i-\overline{x})^2 = \frac{1}{n} \times (\sum^n_{i=1}x_i^2-2\overline{x}(\sum^n_{i=1}x_i) + n\overline{x}^2)\]

那么在 \(\sum xi\) 一定时,\(\sum x_i^2\) 最小时方差最小。
于是 \(f[i][j][k]\) 表示走到 \(i\) 号点,走了 \(j\) 个楼梯,\(\sum x_i\)\(k\) 时,最小的平方和。

#include<bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drp(i, a, b) for (int i = a; i >= b; i--)

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while  (!isdigit(ch)) { if (ch == '-') flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

inline void write(int x) {
    if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
    char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
}

struct edge { int v, w, next; }e[501]; int tot;
int n, g[501];
int dp[51][21][16001], INF = 1000000007;
int deg[51], sum;
int q[5000], r, l = 1;

inline void add(int u, int v, int w) { e[++tot] = edge{ v, w, g[u] }; g[u] = tot; deg[v]++; sum += w; }

#define Min(a, b) a = min(a, b)
void topu() {
    rep(i, 1, n) if(!deg[i]) q[++r] = i;
    dp[1][0][0] = 0;
    while(l <= r) {
        int u = q[l++];
        rep(j, 0, 19) rep(k, 0, sum) if(dp[u][j][k] ^ INF) for(int i = g[u]; i; i = e[i].next) {
            int v = e[i].v, w = e[i].w;
            if(k + w <= sum) Min(dp[v][j + 1][k + w], dp[u][j][k] + w * w);
        }
        for(int i = g[u]; i; i = e[i].next) if(!--deg[e[i].v]) q[++r] = e[i].v;
    }
}

int main() {
    n = read(); int m = read();
    while(m--) { int u = read(), v = read(); add(u, v, read()); }
    memset(dp, 127, sizeof dp); INF = dp[0][0][0];
    topu();
    double ans = 1e9;
    rep(j, 1, 20) rep(k, 0, sum) Min(ans,(dp[n][j][k] * 1.0 / j) - (k * 1.0 * k) / (j * 1.0 * j));
    printf("%.4lf", ans);
    return 0;
}

转载于:https://www.cnblogs.com/aziint/p/8416446.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值