题目大意

具体题面及输入格式戳我!
商店里有\(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. grep 同时满足多个关键字、满足任意关键字和排除关键字

    1. 同时满足多个关键字 grep "word1" file_name | grep "word2" | grep "word3" 2. 满 ...

  2. [Python Study Notes]电池信息

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

  3. thinkPHP数据库操作

    thinkPHP如果要对数据库操作,一般来说首先要做的是在配置文件中链接数据库,然后用M方法实例化一张表,然后就是对表的操作了 可以开启调试功能查看程序执行的sql语句: 1.开启调试功能(默认是已经 ...

  4. IDEA的Maven依赖如何引入到External Libraries中

    现象 在Apollo项目中,遇到了一个问题.当在Module的pom.xml中引入依赖: <dependency> <groupId>com.ctrip.framework.a ...

  5. linux 添加静态路由

    Linux下静态路由修改命令方法一:添加路由route add -net 192.168.0.0/24 gw 192.168.0.1route add -host 192.168.1.1 dev 19 ...

  6. Docker Centos6 下建立 Docker 桥接网络

    cd /etc/sysconfig/network-scripts/; cp ifcfg-eth0  ifcfg-br0 vi ifcfg-eth0 //增加BRIDGE=br0,删除IPADDR,N ...

  7. [转]【C#】分享一个弹出浮动层,像右键菜单那样召即来挥则去

    适用于:.net2.0+ Winform项目 背景: 有时候我们需要开一个简单的窗口来做一些事,例如输入一些东西.点选一个item之类的,可能像这样: 完了返回原窗体并获取刚刚的输入,这样做并没有什么 ...

  8. 洛谷 P4016负载平衡问题【费用流】题解+AC代码

    洛谷 P4016负载平衡问题 P4014 分配问题[费用流]题解+AC代码 负载平衡问题 题目描述 GG 公司有n个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等.如何用最少搬运量可以使 n ...

  9. NDK开发,如何配置 debug环境

    刚开始做NDK 开发的时候,Android Studio 还没提供了 native C/C++ 设置断点 调试,我们都是通过输出 日志来调试,这样费时耗力.Android Studio 应该是在 2. ...

  10. Yii2 Restful Api 401

    采用Yii2 Restful Api方式为APP提供数据,默认你已经做好了所有的编码和配置工作.采用Postman测试接口: 出现这个画面的一个可能原因是:access_token的写法有误,如果你使 ...