BZOJ 4380 [POI2015]Myjnie | DP

链接

BZOJ 4380

题面

有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]。
有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了。
请给每家店指定一个价格,使得所有人花的钱的总和最大。

Input
第一行包含两个正整数n,m(1<=n<=50,1<=m<=4000)。
接下来m行,每行包含三个正整数a[i],b[i],ci

Output
第一行输出一个正整数,即消费总额的最大值。
第二行输出n个正整数,依次表示每家洗车店的价格p[i],要求1<=p[i]<=500000。
若有多组最优解,输出任意一组。

题解

这道题……是一个正常的(?)区间DP。

先将所有c[i]离散化。

f[i][j][k]表示区间[i, j]内的店、最低价格为k,能取到的最大价值。这个最大价值包括所有“完全包含在[i, j]”内的顾客提供的价值。

g[i][j][k]表示区间[i, j]内的店、最低价格>=k,能取到的最大价值。

转移的时候,枚举最低价格k所在的位置p,采取类似分治的思想,设完全包含在[i, j]内、横跨位置p、且c[i] >= k的顾客有cnt[p][k]个,那么有:
\[f[i][j][k] = max_{p = i}^{j} g[i][p - 1][k] + g[p + 1][j][k] + k * cnt[p][k]\]

这个cnt数组在每次枚举[i, j]的时候都预处理一下。

因为要输出方案,所以额外记录一下最优解都是在哪里取到的,最后dfs求出方案。

总复杂度是\(O(n^3m)\)。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define enter putchar('\n')
#define space putchar(' ')
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 52, M = 4002;
int n, m, a[M], b[M], c[M], cnt[N][M], ans[N], lst[M], idx, f[N][N][M], g[N][N][M], gk[N][N][M];
short fp[N][N][M];

void dfs(int l, int r, int k){
    int val = gk[l][r][k], pos = fp[l][r][val];
    ans[pos] = lst[val];
    if(pos > l) dfs(l, pos - 1, val);
    if(pos < r) dfs(pos + 1, r, val);
}

int main(){

    read(n), read(m);
    for(int i = 1; i <= m; i++)
        read(a[i]), read(b[i]), read(c[i]), lst[i] = c[i];
    sort(lst + 1, lst + m + 1);
    idx = unique(lst + 1, lst + m + 1) - lst - 1;
    for(int i = 1; i <= m; i++)
        c[i] = lower_bound(lst + 1, lst + idx + 1, c[i]) - lst;
    for(int i = n; i; i--)
        for(int j = i; j <= n; j++){
            for(int k = 1; k <= idx; k++)
                for(int h = i; h <= j; h++)
                    cnt[h][k] = 0;
            for(int k = 1; k <= m; k++)
                for(int h = i; h <= j; h++)
                    if(a[k] >= i && a[k] <= h && b[k] >= h && b[k] <= j)
                        cnt[h][c[k]]++;
            for(int h = i; h <= j; h++)
                for(int k = idx; k; k--)
                    cnt[h][k] += cnt[h][k + 1];
            for(int k = 1; k <= idx; k++)
                for(int p = i; p <= j; p++)
                    if(g[i][p - 1][k] + g[p + 1][j][k] + lst[k] * cnt[p][k] >= f[i][j][k])
                        fp[i][j][k] = p, f[i][j][k] = g[i][p - 1][k] + g[p + 1][j][k] + lst[k] * cnt[p][k];
            for(int k = idx; k; k--){
                if(g[i][j][k + 1] > f[i][j][k])
                    gk[i][j][k] = gk[i][j][k + 1], g[i][j][k] = g[i][j][k + 1];
                else
                    gk[i][j][k] = k, g[i][j][k] = f[i][j][k];
            }
        }
    write(g[1][n][1]), enter;
    dfs(1, n, 1);
    for(int i = 1; i <= n; i++)
        write(ans[i]), i == n ? enter: space;

    return 0;
}

原文地址:https://www.cnblogs.com/RabbitHu/p/BZOJ4380.html

时间: 2024-10-11 23:21:27

BZOJ 4380 [POI2015]Myjnie | DP的相关文章

bzoj [POI2015]Myjnie

[POI2015]Myjnie Time Limit: 40 Sec Memory Limit: 256 MBSec Special Judge Description 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]. 有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了. 请给每家店指定一个价格,使得所有人花的钱的总和最大. Input 第一行包含两个正整数

[POI2015]Myjnie

[POI2015]Myjnie 题目大意: 有\(n(n\le50)\)家洗车店从左往右排成一排,每家店都有一个正整数价格\(d_i\). 有\(m(m\le4000)\)个人要来消费,第\(i\)个人会选择\(a_i\sim b_i\)这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于\(c_i\),那么这个人就不洗车了. 请给每家店指定一个价格,使得所有人花的钱的总和最大. 思路: 将\(c\)离散化后进行区间DP. 用\(f_{i,j,k}\)表示区间\([i,j]\)最小值为

BZOJ 1087状态压缩DP

状态压缩DP真心不会写,参考了别人的写法. 先预处理出合理状态, 我们用二进制表示可以放棋子的状态,DP[I][J][K]:表示现在处理到第I行,J:表示第I行的状态,K表示现在为止一共放的棋子数量. #include<stdio.h> #include<iostream> #define N 1111 using namespace std; typedef long long ll; int num,n,m; ll dp[11][1<<11][90]; int hh

BZOJ 1237 配对(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1237 题意:给出两个n元素的数列A和B.为A中的每个元素在B中为其找一个配对的元素(配对的元素不能相等,B中每个元素只能被使用一次),使得所有配对的两个数的差的绝对值之和最小? 思路:首先排序,若无配对元素不能相等这一 限制,则排序后一一匹配即可.有了这个限制,那么对于相等的元素就要在其前后进行调整,显然,与距离较远的调整不如与距离近的调整优.那么,需要在多大的 范围内调整才能保证最优

bzoj 3437 斜率优化DP

写题解之前首先要感谢妹子. 比较容易的斜率DP,设sum[i]=Σb[j],sum_[i]=Σb[j]*j,w[i]为第i个建立,前i个的代价. 那么就可以转移了. 备注:还是要感谢妹子. /************************************************************** Problem: 3437 User: BLADEVIL Language: C++ Result: Accepted Time:3404 ms Memory:39872 kb **

@bzoj - [email&#160;protected] [POI2015] Myjnie

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 家洗车店从左往右排成一排,每家店都有一个正整数价格 p[i]. 有 m 个人要来消费,第 i 个人会驶过第 a[i] 个开始一直到第 b[i] 个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于 c[i],那么这个人就不洗车了. 请给每家店指定一个

[BZOJ 3791] 作业 【DP】

题目链接:BZOJ - 3791 题目分析 一个性质:将一个序列染色 k 次,每次染连续的一段,最多将序列染成 2k-1 段不同的颜色. 那么就可以 DP 了,f[i][j][0|1] 表示到第 i 个位置,染了 j 段,当前这一段颜色为 0|1 的最大价值. f[i][][] 只与 f[i-1][][] 有关,第一维用滚动数组就可以了. 代码 #include <iostream> #include <cstdio> #include <cstring> #inclu

[BZOJ 2165] 大楼 【DP + 倍增 + 二进制】

题目链接:BZOJ - 2165 题目分析: 这道题我读了题之后就想不出来怎么做,题解也找不到,于是就请教了黄学长,黄学长立刻秒掉了这道题,然后我再看他的题解才写出来..Orz 使用 DP + 倍增 ,用状态 f[x][i][j] 表示从 i 出发,坐 x 次电梯到达 j ,最多能上升的层数.开始读入的就是 f[1][][] 数组.(注意:若开始时 i 不能走到 j , 则 f[1][i][j] = -INF) 使用倍增,用 f[x][][] 求出 f[x << 1][][] , 一直求f[2

BZOJ 1040: [ZJOI2008]骑士 [DP 环套树]

传送门 题意:环套树的最大权独立集 一开始想处理出外向树树形$DP$然后找到环再做个环形$DP$ 然后看了看别人的题解其实只要断开环做两遍树形$DP$就行了...有道理! 然后洛谷时限再次不科学,卡常失败$SAD$ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; co