题目大意

具体题面及输入格式戳我!
商店里有\(N\)种药水,每种药水都有一个售价和回收价。
小\(S\) 攒了\(V\)元钱,还会\(M\)种魔法,可以把一些药水合成另一种药水。
他在第一天可以购买药水、使用最多\(K\)次魔法,然后第二天在把手头上的药水出售。
现在小\(S\)希望知道两天之内他最多能够赚多少钱?

样例输入:
\(4\ \ 2\ \ 6\ \ 3\)
\(1\ \ 0\)
\(1\ \ 0\)
\(5\ \ 3\)
\(20\ \ 15\)
\(3\ \ 2\ \ 1\ \ 2\)
\(4\ \ 3\ \ 1\ \ 2\ \ 3\)
样例输出:
\(12\)
样例解释:
先购买两个物品1与物品2,合成得到两个物品3 , 花费4元,使用2次魔法
再购买一个物品1与物品2,与其中一个物品3合并出一个物品4,花费2元,使用1次魔法。
到了第二天,卖出当前手头上的一个物品3与一个物品4,获利 15+3 = 18元。
总共使用了2+1=3次魔法,花费4+2=6元,获利 (15+3)-(4+2) = 12元,为最优方案。

数据范围:\(N\leq 60\) ; \(M\leq 240\) ; \(V\leq 10^3\) ; \(K\leq30\) ;

思路与解法

这么好的题目Luogu上竟然没人做,非常全面的一道\(DP\)题。
注意到物品的合成是可以嵌套进行的(用合成的东西去合成新的东西)。
这个就非常的恶心。

\(Solve_a\)

我们假设现在合成只能有一层,即不能用合并出来的东西去合并新的东西。
那么这就是一道非常简单的二维背包了。 所以我们考虑如何消除嵌套合并的影响。
设\(f[i][j]\)表示使用\(j\)次魔法得到物品\(i\)的最低费用。显然初始\(f[i][0] = price[i]\)
那么发现无法直接转移\(f\),但是对于每一个魔法我们是可以\(DP\)的:
我们枚举一个魔法,设\(r[i][j]\)表示得到这个魔法的前i个原料,使用了j次魔法的最低价格.
那么转移:
\[r[\ i\ ][\ j_1+j_2\ ] = r[i-1][j_1] + f[\ itm[i]\ ][j_2]\]
其中\(itm[i]\)为这个魔法的第\(i\)个原料,\(f\)的含义见上。
转移完后用\(r[i][j]\)更新\(f[i][j+1]\)(把这些原料合成又需要一次魔法)即可。
发现一个问题,\(f\)是我们正在求的东西,但是转移中又出现了\(f\)。
一个最直接的想法就是\(TopSort\)一遍再\(DP\),但是此题数据非常恶心每组数据的合成关系都出现了环。
发现我们最多只需要做\(N\)次,所有的\(f\)就都到位了,所以就做\(N\)遍这个\(DP\)即可。
这个部分的时间复杂度为\(O(NMK^2)\),难点在于想到使用\(r\)数组。

\(Solve_b\)

求出了\(f\)数组,剩下的部分就是一个很简单的二维背包了。
\(g[i][j][k]\)表示前\(i\)个物品,使用了\(j\)元,用了\(k\)次魔法的最大利润。
把每一个\(f[i][r]\)看成一个物体,枚举使用次数\(e\)即可:
\[g[i][j][k] = max(\ g[i-1][\ j-e*f[i][r]\ ][\ k-e*r\ ] + profit(i)*e\ )\]
直接这样转移复杂度过高,使用完全背包问题的状态优化,把状态优化为二维\(g[j][k]\)即可,注意枚举顺序。
这个部分时间复杂度为\(O(NK^2V)\),总时间复杂度为\(O(\ NK^2(M+V)\ )\)。

实现代码:

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define pb push_back
#define ll long long
#define gi(x) scanf("%lld",&x);
#define fp(i,j,k) for(RG ll i = j; i <= k; i ++)
#define INF 1e9+7
using namespace std;

ll f[65][35] , r[65][35] , g[1005][35] ;
ll N , M , V , K , pft[65] , cnt;
ll itm[250][605] , sum[250] , Ans; vector<ll>gp[ 65 ]; 

IL void Min(RG ll &x,RG ll y){if(x > y)x = y;}
IL void Max(RG ll &x,RG ll y){if(x < y)x = y;}
const ll zr = 0;

IL void DP1(RG int u){
    RG ll szz = gp[u].size();
    for(RG ll sq = 0; sq < szz; sq ++){
        RG ll id = gp[u][sq];
        fp(i,0,sum[id])fp(j,0,K)r[i][j] = INF;
        r[0][0] = 0;
        for(RG ll i = 1; i <= sum[id]; i ++)
            for(RG ll j1 = 0; j1 <= K; j1 ++)
                for(RG ll j2 = 0; j2 + j1 <= K; j2 ++){
                    Min( r[i][j1+j2] , r[i-1][j1] + f[ itm[id][i] ][j2] );
                }
        for(RG ll j = 0; j <= K-1; j ++)
            Min(f[u][j+1] , r[sum[id]][j]);
    }return;
    //f[u][j]: 表示使用j次魔法,得到u物品的最小花费
    //r[i][j]: 表示得到此魔法前的前i个原料,使用了j次魔法的最小花费。
}

IL void DP2(){
    for(RG ll i = 1; i <= N; i ++)
        for(RG ll e = 0; e <= K; e ++)    //枚举物品
            for(RG ll k = e; k <= K; k ++)      //用了多少次魔法
                for(RG ll val = f[i][e]; val <= V; val ++)  //预估花费
                    Max(g[val][k],g[val-f[i][e]][k-e] + pft[i] ) , Max(Ans , g[val][k]-val);
    // g[i][j][k] 表示前i个物品,花费了j元 ,使用了k次魔法 的最大利润。
    return;
}

int main(){
    gi(N); gi(M); gi(V); gi(K);
    fp(i,0,N)fp(j,0,K)f[i][j] = INF;
    for(RG ll i = 1; i <= N; i ++){ gi(f[i][0]); gi(pft[i]);}
    for(RG ll i = 1; i <= M; i ++){
        RG ll x,y; gi(x); gi(y);
        sum[i] = y;
        for(RG ll j = 1; j <= y; j ++)gi(itm[i][j]);
        gp[x].pb(i);
    }
    for(RG int i = 1; i <= N; i ++)
        for(RG int e = 1; e <= N ; e ++)
            DP1(e);
    Ans = -INF;
    DP2(); cout << max(Ans , zr);  return 0;
}

Luogu P1860 新魔法药水的更多相关文章

  1. 洛谷P1860——新魔法药水

    传送门:QAQQAQ 题意:商店里有N种药水,每种药水都有一个售价和回收价.小S攒了V元钱,还会M种魔法,可以把一些药水合成另一种药水.他一天可以使用K次魔法,问他一天最多赚多少钱? N<=60 ...

  2. [P1860]新魔法药水

    题目描述 商店里有N种药水,每种药水都有一个售价和回收价.小S攒了V元钱,还会M种魔法,可以把一些药水合成另一种药水.他一天可以使用K次魔法,问他一天最多赚多少钱? 输入输出格式 输入格式: 第一行四 ...

  3. 洛谷P1860 新魔法药水

    洛谷题目链接 动态规划: 这个题目调了我好久....结果循环变量写错了... 而且题目有个坑!!!只能用开始给你的$v$元买入东西 回归正题: 我们定义状态$ans[i][j]$表示第$i$个物品用了 ...

  4. [luogu P3787][新创无际夏日公开赛] 冰精冻西瓜 [树状数组][dfs序]

    题目背景 盛夏,冰之妖精琪露诺发现了一大片西瓜地,终于可以吃到美味的冻西瓜啦. 题目描述 琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地.这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有 ...

  5. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

  6. NOIP前的刷题记录

    因为这几天要加油,懒得每篇都来写题解了,就这里记录一下加上一句话题解好了 P4071 [SDOI2016]排列计数   组合数+错排 loj 6217 扑克牌 暴力背包 P2511 [HAOI2008 ...

  7. test20190803 夏令营NOIP训练19

    60+100+0=160 贪婪大陆 面对蚂蚁们的疯狂进攻,小FF的Tower defence宣告失败--人类被蚂蚁们逼到了Greed Island上的一个海湾.现在,小FF的后方是一望无际的大海, 前 ...

  8. [Luogu 3258] JLOI2014 松鼠的新家

    [Luogu 3258] JLOI2014 松鼠的新家 LCA + 树上差分. 我呢,因为是树剖求的 LCA,预处理了 DFN(DFS 序),于是简化成了序列差分. qwq不讲了不讲了,贴代码. #i ...

  9. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

随机推荐

  1. java递归

    package com.sun.test; import java.util.ArrayList; import java.util.List; /** * 递归 * */ public class ...

  2. 洛谷P1962 斐波那契数列【矩阵运算】

    洛谷P1962 斐波那契数列[矩阵运算] 题目背景 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) ( ...

  3. C控制语句:分支和跳转

    小技巧:程序return前加个getchar();可以让程序停住.%%可以打印使printf()中打印出%号 #include<stdio.h>#define SPACE ''int ma ...

  4. Java集合中的AbstractMap抽象类

    jdk1.8.0_144 AbstractMap抽象类实现了一些简单且通用的方法,本身并不难.但在这个方法中有两个方法非常值得关注,keySet和values方法源码的实现可以说是教科书式的典范. 抽 ...

  5. ActiveMQ、Stomp、SockJS入门级应用

    使用ActiveMQ.Stomp.SockJS实现实时在线聊天 ActiveMQ : 强大的开源即时通讯和集成模式的服务器.在本项目中充当消息代理服务器,stomp协议服务端. 安装:在官网下载,直接 ...

  6. window7 x64 vs2015 如何编译 libqr 二维码生成库?

    1.下载libqr库 下载地址:https://github.com/rsky/qrcode 注:因 libqr 依赖 zlib 库,所以首先编译 zlib库 zlib 库编译指南:http://ww ...

  7. EmguCV 绘画图形

    1.Image类中绘图常用函数列表 实践验证 ///初始化图片 private void Form1_Load(object sender, EventArgs e) { oldpic = new E ...

  8. Redis--配置密码

    可以通过以下方法进行密码的配置: ① 修改配置文件设置密码 ② 通过命令修改密码(重启redis后,新设置的密码会失效) 此处介绍第一种 1. 找到redis的配置文件,一般在/etc/redis.c ...

  9. Win11曝光 代号“Core OS” 明年推出-IT外包

    Win10发布距今已经有两年半的时间了,微软对其"修修补补"也有五次了,而第六次的更新也正在进行中.根据消息爆料,Win 11级别的开发项目"Windows Core O ...

  10. 【前端】用百度BAE和express部署自己的node后台

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/node_bae.html 百度有一个应用引擎,价格非常便宜,Java的tomcat每天4毛钱,node每天2毛钱, ...