【简●解】[HNOI2005]星际贸易

【大意】

太多了,懒得打,贴\(LG\)的图了。。。

1361.png

1362.png

【分析】

开始拿到这道题有点慌:怎么限制条件这么多,再读读题。

注意一个东西,就是贸易额与费用是独立分开的,并且题目保证只有一种方案获得最大贸易额。

所以我们\(dp\)也可以分开\(dp\)

对于贸易额,每走到一个星球,我可以选择卖或不卖,经典的背包问题,直接\(f[i][j]=min\{f[i-1][j-A_i]+B_i\}\)

然后从后往前扫记录决策点的转移,这些星球是必到的,即必定会在该星球上维修。

现在来看费用。

\(g[i][j]\)表示到第\(i\)个星球,剩余反物质燃料为\(j\)的最小费用,暴力枚举转移点,则有\(g[i][j]=\{g[k][l]+(j-l+2)\times P_i+T_i\}(0\le k<i,L_i-L_k\le L_0,l\le j)\)

\(O(n^4)\)转移。。。

然后可推出一个更优的式子:\(g[i][j]=min\{g[k][j+2]+F_i,g[i][j-1]+P_i\}\)

\(O(n^3)\)转移。。。

然后,,,我就嫖题解了。。。。

噢,原来可以单调队列优化啊,对于每个\(j\)开一个单调队列,若当前的\(i\)为必到的星球,就清空队列中所有的元素,然后把\(i\)放进去。

\(O(n^2)\)转移!!!

【Code】

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Re register
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXR = 4000 + 5;
const int N = 2000 + 5;
const int M = 2000 + 5;
inline int read(){
    int f = 1, x = 0; char ch;
    do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9');
    do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    return f * x;
}
int n, m, r, L0, maxm, maxr, A[N], B[N], L[N], P[N], F[N], f[N][M], g[N][M], vis[N]; 
deque <int> Q[MAXR];
int main(){
    n = read(), m = read(), r = read(), L0 = read(); 
    if (r > (n << 1)) r = (n << 1);
    for (int i = 1;i <= n; ++i) {
        A[i] = read(), B[i] = read(), L[i] = read(), P[i] = read(), F[i] = read();
        if (L[i] - L[i - 1] > L0) {
            puts("Poor Coke!");
            return 0;
        }
    }
    memset(f, 128, sizeof f);
    f[0][0] = 0;
    for (int i = 1;i <= n; ++i) {
        for (int j = 0;j <= m; ++j) {
            if (f[i - 1][j] >= 0) f[i][j] = f[i - 1][j];
            if (j >= A[i]) {
                if (f[i][j] < f[i - 1][j - A[i]] + B[i]) {
                    f[i][j] = f[i - 1][j - A[i]] + B[i];
                }
            }
        }
    }
    
    for (int i = 1;i <= m; ++i) if (f[n][i] > f[n][maxm]) maxm = i; 
    
    for (int i = n, val = maxm;i >= 1; --i) {
        if (val - A[i] >= 0 && f[i][val] == f[i - 1][val - A[i]] + B[i]){
            vis[i] = 1;
            val -= A[i];
        }
    }
    
    memset(g, 0x3f, sizeof g);
    g[0][r] = 0;
    Q[r].push_back(0);
    for (int i = 1;i <= n; ++i) {
        for (int j = 0;j <= r; ++j) {
            if (P[i] > 0 && j > 0) {
                g[i][j] = min(g[i][j], g[i][j - 1] + P[i]);
            }
            
            while (!Q[j].empty() && L[i + 1] - L[Q[j].front()] > L0) Q[j].pop_front();
            
            if (!Q[j + 2].empty()) {
                g[i][j] = min(g[i][j], g[Q[j + 2].front()][j + 2] + F[i]);
            }
            
            if (vis[i]) Q[j].clear();
            
            while (!Q[j].empty() && g[Q[j].back()][j] >= g[i][j]) Q[j].pop_back();
            
            Q[j].push_back(i);
        }
    }
    for (int i = 1;i <= r; ++i) if (g[n][i] < g[n][maxr]) maxr = i;
    
    if (g[n][maxr] == INF) printf("Poor Coke!\n");
    else printf("%d %d\n", f[n][maxm], f[n][maxm] - g[n][maxr]);    
    
    return 0;
}

转载于:https://www.cnblogs.com/silentEAG/p/11433991.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值