Bzoj P2424 [HAOI2010]订货___斜率优化dp/费用流

127 篇文章 0 订阅
5 篇文章 0 订阅

题目大意:

iUidi 某 产 品 第 i 个 月 需 求 量 为 U i , 订 货 单 价 为 d i 。
m 上 个 月 月 底 未 销 完 的 单 位 产 品 要 付 存 贮 费 用 m 。
10n0 假 定 第 1 月 月 初 的 库 存 量 为 0 , 第 n 月 月 底 的 库 存 量 也 为 0 。
n使 问 如 何 安 排 这 n 个 月 订 购 计 划 , 才 能 使 成 本 最 低 ?
每 月 月 初 订 购 , 订 购 后 产 品 立 即 到 货 , 进 库 并 供 应 市 。
S 于 当 月 被 售 掉 则 不 必 付 存 贮 费 。 假 设 仓 库 容 量 为 S 。

0<=n<=50,0<=m<=10,0<=S<=10000 0 <= n <= 50 , 0 <= m <= 10 , 0 <= S <= 10000
0<=Ui<=10000 0 <= U i <= 10000
0<=di<=100 0 <= d i <= 100

分析:

动态规划:
f[i][j]ij 设 f [ i ] [ j ] 表 示 到 了 第 i 个 月 底 , 仓 库 里 存 货 数 量 为 j 时 的 最 低 成 本 。
显然的
f[i][j]=min{f[i1][k]+km+(u[i]+jk)d[i]} f [ i ] [ j ] = m i n { f [ i − 1 ] [ k ] + k ∗ m + ( u [ i ] + j − k ) ∗ d [ i ] }
可是这样的话,我们的时间复杂度可能就会达到 O(NS2) O ( N S 2 )
我们发现,斜率优化一下,将 k k 移 项
可以得到
f[i][j]=min{f[i1][k]+(md[i])k+(u[i]+j)d[i]} f [ i ] [ j ] = m i n { f [ i − 1 ] [ k ] + ( m − d [ i ] ) ∗ k + ( u [ i ] + j ) ∗ d [ i ] }
那么我们可以同步枚举 j,k j , k
时间复杂度就是 O(NS) O ( N S )

费用流:
连边 S>i S − > i ,流量无穷, d[i] 单 位 费 用 为 d [ i ]
连边 i>T i − > T ,流量为 U[i] U [ i ] 0 单 位 费 用 为 0
连边 i>i+1 i − > i + 1 ,流量为 S S m
然后同时建反向边,跑最小费用最大流

代码:

斜率优化dp:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define INF 0x7fffffff
#define M 10005
#define N 55

using namespace std;

int f[N][M], u[N], d[N], n, m, S;

int main() {
    scanf("%d %d %d", &n, &m, &S);
    for (int i = 1; i <= n; i++) scanf("%d", &u[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
    for (int i = 0; i <= S; i++)  
         f[1][i] = (u[1] + i) * d[1];
    for (int i = 2; i <= n; i++) {
         int k = 0, cmin = INF;
         for (int j = 0; j <= S; j++) {
              for (; k <= min(u[i] + j, S); k++) 
                   cmin = min(cmin, f[i - 1][k] + (m - d[i]) * k);
              f[i][j] = cmin + (u[i] + j) * d[i];
         }
    }
    printf("%d\n", f[n][0]);
    return 0;
}


费用流:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define INF 0x7fffffff
#define N 55

using namespace std;

struct edge {
    int to, num, cost, nt;
}e[6 * N];

int incf[N], dis[N], pre[N], Ls[N], v[N], n, m, C, S, T, cnt = 1;
int Max_flow = 0, Ans_cost = 0;

void Addedge(int u, int v, int w, int f) {
    e[++cnt].to = v, e[cnt].num = w, e[cnt].cost = f, e[cnt].nt = Ls[u], Ls[u] = cnt; 
    e[++cnt].to = u, e[cnt].num = 0, e[cnt].cost = -f, e[cnt].nt = Ls[v], Ls[v] = cnt;  
}

bool spfa(int s, int t) {
    for (int i = s; i <= t; i++) dis[i] = INF;
    memset(v, 0, sizeof(v));
    queue <int> Q; 
    Q.push(s); 
    v[s] = 1; dis[s] = 0; 
    incf[s] = INF;
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for (int i = Ls[u]; i ; i = e[i].nt) 
             if (e[i].num && dis[e[i].to] > dis[u] + e[i].cost){
                 dis[e[i].to] = dis[u] + e[i].cost;
                 incf[e[i].to] = min(incf[u], e[i].num);
                 pre[e[i].to] = i;
                 if (!v[e[i].to]) {
                     Q.push(e[i].to);
                     v[e[i].to] = 1;
                 }   
             }
        v[u] = 0;
    }
    return dis[t] != INF;
}

void update(int s, int t) {
    int x = t;
    while (x != s) {
        int i = pre[x];
        e[i].num -= incf[t];
        e[i^1].num += incf[t];
        x = e[i^1].to;
    }   
    Max_flow += incf[t];
    Ans_cost += dis[t] * incf[t];
}

int EK(int s, int t) {
    while (spfa(s, t)) update(s, t);
}

int main() {
    scanf("%d %d %d", &n, &m, &C);
    int x; S = 0, T = n + 1;
    for (int i = 1; i <= n; i++) {
         scanf("%d", &x);
         Addedge(i, T, x, 0);
    }
    for (int i = 1; i <= n; i++) {
         scanf("%d", &x);
         Addedge(S, i, INF, x);
    }
    for (int i = 1; i <= n - 1; i++) 
         Addedge(i, i + 1, C, m);
    EK(S, T);
    printf("%d\n", Ans_cost);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值