P1336 最佳课题选择 题解

状态:考虑\(f_{i,j}\)表示前\(i\)种论文里面,一共写了\(j\)篇,的最少花费时间。

转移策略:我们一次考虑每一种论文写多少篇。假设写\(k\)篇,\(k \in [0,j] \cap \mathbb{Z}\) ,有转移方程:

\[f_{i,j} = min(f_{i-1,j-k} + cost(i,k)), k \in [0,j] \cap \mathbb{Z}
\]

其中

\[cost(i,k) = a_i \times k ^ {b_i}
\]

可以做记忆化优化,因为多次访问同一个\(cost(i,k)\)。

初始化:我们考虑选0篇无论怎样花费为0,选0种论文但多于0篇一定是不可能的,花费为Inf,再加上转移中要取min的缘故,我们初始化为:

\[\begin{equation*}
f_{i,j}=\left\{
\begin{array}{}
0 & ,j=0\\
+ \inf &, j \neq 0\\
\end{array}
\right
.
\end{equation*}
\]

相关变量范围见代码。

最后,我们就可以开滚动数组,或者倒序\(j\)优化掉一维。

代码在文末。


这里和01背包及无限背包做个对比。

01背包的转移选择策略,是一个一个选(同时也是一种一种选,因为一种里面只有一个)。转移方程:

\[f_{i,j} = max(f_{i-1,j}, f_{i-1,j-c_i} + v_i)
\]

无限背包的转移选择策略,是在一种里面一个个选。转移方程:

\[f_{i,j} = max(f_{i-1,j}, f_{i,j-c_i} + v_i)
\]

可以看到两者细微的差别在,选物品的时候,01背包访问\(i-1\),无限背包访问\(i\),所以可以把其中一个维度压缩掉之后,01反着做,无限正着做。

本题目有两点不同:

第一,本题相当于“选择超过某价值的物品,使得容量需要量最小”,把状态的内外对换了一对。

第二,本题目中,如果选了前面一个,那么后面再选的时候,贡献会不一样,因为时间贡献是幂级的而非线性的。类比到无限背包中就是“选的越多,价值越少”,已经选了多少会影响到后面选的东西。选同一个东西的贡献,在不同情况下不一样。

于是,按照无限背包“一个一个”选的策略,就会涉及到之前的选择情况,我们尝试把第\(i\)种选择数目\(k\)加入到状态中,有

\[\begin{equation*}
f_{i,j,k(k \leq j)}=\left\{
\begin{array}{}
f_{i,j-1,k-1} - cost(i,k-1) + cost(i,k) & ,k \neq 0\\
min(f_{i-1,j,l}), l \in [0,j] \cap \mathbb{Z} &, k = 0\\
\end{array}
\right
.
\end{equation*}
\]

这样是正确的,但是我们发现大多数的状态被浪费了,因为\(k \neq 0\) 的时候只有一种选择。

还有一种方式是把\(f_{i,j}\)中选择了多少个第i种论文,另存为\(g_{i,j} = k\),于是我们有转移方程(错误的):

\[\begin{equation*}
f_{i,j}=min\left(
\begin{array}{}
f_{i,j-1} - cost(i,g_{i,j-1}) + cost(i,g_{i,j}), g_{i,j} = g_{i,j-1} + 1 ,\\
f_{i-1,j}, g(i,j) = 0
\end{array}
\right)
\end{equation*}
\]

这个转移方程看上去好像是对的,因为就是按照无限背包的思路改的,但实际上是错误的,提交后只能有10分。因为理论上这种转移方程只能处理线性的\(cost(i,k)\),即\(b_{i} = 1\)或\(b_{i} = 0\)

我们和无限背包进行对比:假设有\(f_{1,1} = 12\),\(f_{1,2} = 24\),\(f_{2,1} = 2\),那么对于无限背包问题,有:

\[f_{2,2} = max(f_{1,2},f_{2,1} + v_{2})\\
\because f_{2,1} = max(f_{1,1},f_{2,0} + v_{2}) \neq f_{1,1}\\
\therefore f_{2,1} \geq f_{1,1}\\
\therefore f_{2,1} + v_{2} \geq f_{1,1} + v_{2}
\]

因此\(f_{1,1} + v_{2}\)不会影响\(f_{2,1} + v_{2}\)对\(f_{2,2}\)可能的贡献,也就是不可能贡献(被max掉了)。

但是在这个问题中,我们令\(w_{i,k} = cost(i,k) - cost(i,k-1)\),有:

\[f_{2,2} = min(f_{1,2},f_{2,1} + w_{2,1})\\
\because f_{2,1} = min(f_{1,1},f_{2,0} + w_{2,2}) \neq f_{1,1}\\
\therefore f_{2,1} \leq f_{1,1}\\
\therefore f_{2,1} + w_{2,2} \quad? \quad f_{1,1} + w_{2,1}
\]

可以发现我们无法判断\(f_{1,1} + w_{2,1}\)对于\(f_{2,2}\)是否有贡献,其根源在于对于同一个\(i\),\(w_{i,k}\)是可能不同的,因此不等式失效,即在这种状态和转移方程下,不满足最优子结构。因此DP失效。

(这也说明,一个可DP的问题是存在“最优子结构”“无后效性”,只有特定的方式去“成立”这些性质才能DP,不是随便搞一搞就一定可以DP的)

也就是说,在无限背包问题中,我们把一个物品拿走,从规模更小的状态中转移,这个物品的贡献是确定的,决定“拿走”这一选择中的最优解,就只能是承接之前的最优解。

然而在这个问题中,如果我们把一个物品拿走,从规模更小的状态中转移时,这个物品的贡献是不确定的,也就是说除了最优解以外,次有解也有可能贡献出更好的结果,因此“最优子结构失效了”。

因此,我们在考虑状态和状态转移方程时,可以灵活更改状态、转移方式和策略、记录信息等等,同时也要按照普适状况考虑转移的合法性和具体转移方法。


标准AC代码:

#include <bits/stdc++.h>

#define N (int)(205)
#define M (int)(25) using namespace std; typedef long long LL; int n,m;
LL f[N];
LL a[M],b[M];
LL p[M][N]; LL cost(int i, int k)
{
if(p[i][k] == -1)
return p[i][k] = a[i] * pow(k,b[i]);
else
return p[i][k];
} int main()
{
memset(p,-1,sizeof(p));
memset(f,0x7f,sizeof(f));
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n >> m;
for(int i = 1;i <= m;i++) cin >> a[i] >> b[i];
f[0] = 0;
for(int i = 1;i <= m;i++)
{
for(int j = n;j >= 1;j--)
{
for(int k = 0;k <= j;k++)
{
f[j] = min(f[j],f[j-k] + cost(i,k));
}
}
}
cout << f[n];
return 0;
}

另一种AC的代码:

#include <bits/stdc++.h>

#define N (int)(205)
#define M (int)(25) using namespace std; typedef long long LL; int n,m;
LL f[M][N][N];
LL a[M],b[M];
LL p[M][N]; LL cost(int i, int k)
{
if(p[i][k] == -1)
return p[i][k] = a[i] * pow(k,b[i]);
else
return p[i][k];
} int main()
{
memset(p,-1,sizeof(p));
memset(f,0x7f,sizeof(f));
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n >> m;
for(int i = 1;i <= m;i++) cin >> a[i] >> b[i];
for(int i = 0;i <= m;i++) f[i][0][0] = 0;
for(int i = 1;i <= m;i++)
{
for(int j = 1;j <= n;j++)
{
for(int l = 0;l <= j;l++)
{
f[i][j][0] = min(f[i][j][0],f[i-1][j][l]);
}
for(int k = 1;k <= j;k++)
{
f[i][j][k] = f[i][j-1][k-1] - cost(i,k-1) + cost(i,k);
}
}
} LL ans = 1e18;
for(int k = 0;k <= n;k++)
ans = min(ans,f[m][n][k]);
cout << ans;
return 0;
}

洛谷 P1336 最佳课题选择 题解的更多相关文章

  1. 洛谷 P1336 最佳课题选择

    P1336 最佳课题选择 题目提供者 yeszy 标签 动态规划 福建省历届夏令营 传送门 难度 尚无评定 题目描述 Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择.由于课 ...

  2. P1336 最佳课题选择

    P1336 最佳课题选择 题目描述 Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择.由于课题数有限,Matrix67不得不重复选择一些课题.完成不同课题的论文所花的时间不同 ...

  3. luogu P1336 最佳课题选择

    题目描述 Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择.由于课题数有限,Matrix67不得不重复选择一些课题.完成不同课题的论文所花的时间不同.具体地说,对于某个课题i ...

  4. luogu P1336 最佳课题选择 |背包dp

    题目描述 Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择.由于课题数有限,Matrix67不得不重复选择一些课题.完成不同课题的论文所花的时间不同.具体地说,对于某个课题i ...

  5. 洛谷P1783 海滩防御 分析+题解代码

    洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...

  6. 洛谷P4047 [JSOI2010]部落划分题解

    洛谷P4047 [JSOI2010]部落划分题解 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落 ...

  7. 洛谷P1155 双栈排序题解(图论模型转换+二分图染色+栈)

    洛谷P1155 双栈排序题解(图论模型转换+二分图染色+栈) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1311990 原题地址:洛谷P1155 双栈排序 ...

  8. 02题解-洛谷 P2395 BBCode转换Markdown 题解

    洛谷 P2395 BBCode转换Markdown 题解 题目传送门: here. 一道毒瘤的大模拟,给了你一部分的 BBCode 和 Markdown 语法,叫你转换.如下表: BBCode Mar ...

  9. 浅谈分治 —— 洛谷P1228 地毯填补问题 题解

    如果想看原题网址的话请点击这里:地毯填补问题 原题: 题目描述 相传在一个古老的阿拉伯国家里,有一座宫殿.宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:公主就站在其中一个方格子 ...

  10. 洛谷10月月赛II题解

    [咻咻咻] (https://www.luogu.org/contestnew/show/11616) 令人窒息的洛谷月赛,即将参加NOIp的我竟然只会一道题(也可以说一道也不会),最终145的我只能 ...

随机推荐

  1. websocket多实例推送解决方案-数据实时展示

    需求 需要前端展示实时的订单数据信息.如下图所示,实时下单实时页面统计更新展示 思路方案 前端使用websocket 建立通信 后端监听数据库的binglog变更,实时得到最新数据,推送到前端 现状及 ...

  2. 2021-04-21:手写代码:Dijkstra算法。

    2021-04-21:手写代码:Dijkstra算法. 福大大 答案2021-04-21: Dijkstra算法是一种基于贪心策略的算法.每次新扩展一个路程最短的点,更新与其相邻的点的路程.时间紧,未 ...

  3. 一天吃透SpringCloud面试八股文

    1.什么是Spring Cloud ? Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成.Spring cloud Tas ...

  4. 新出的Alist云盘视频助手,真的香还是假的香?

    作为某云盘的重度使用者和长期受虐者,前段时间无意中看到一款新出的网盘工具,叫Alist云盘视频助手,不同于一般的网盘工具,它不是面向网盘数据下载的,它面向的是网盘视频文件隐私保护,大白话就是:加密网盘 ...

  5. 【重学C++】04 | 说透C++右值引用(上)

    文章首发 [重学C++]04 | 说透C++右值引用(上) 引言 大家好,我是只讲技术干货的会玩code,今天是[重学C++]的第四讲,在前面<03 | 手撸C++智能指针实战教程>中,我 ...

  6. Kubernetes 证书详解(鉴权)

    Kubernetes 证书详解(鉴权) 简介 上一篇 系统分析了 Kubernetes 集群中每个证书的作用和证书认证的原理.对于 Kube-apiserver,Kubelet 来说,它们都能提供 H ...

  7. ODOO13 之九:Odoo 13开发之外部 API – 集成第三方系统

    Odoo 13开发之外部 API – 集成第三方系统 Odoo 服务器端带有外部 API,可供网页客户端和其它客户端应用使用.本文中我们将学习如何在我们的客户端程序中使用 Odoo 的外部 API.为 ...

  8. mysql 有关账号登录和重新设置密码操作

    #进入mysql客户端$mysqlmysql> select user(); #查看当前用户mysql> exit # 也可以用\q quit退出 # 默认用户登陆之后并没有实际操作的权限 ...

  9. 文心一言 VS 讯飞星火 VS chatgpt (27)-- 算法导论5.1 2题

    二.请描述 RANDOM(a,b)过程的一种实现,它只调用 RANDOM(0,1).作为a和b的函数,你的过程的期望运行时间是多少? 文心一言: 假设我们有一个伪随机数生成器,可以生成在[0,1)范围 ...

  10. 我在 vscode 插件里接入了 ChatGPT,解决了代码变量命名的难题

    lowcode 插件 已经迭代了差不多3年.作为我的生产力工具,平常一些不需要动脑的搬砖活基本上都是用 lowcode 去完成,比如管理脚手架,生成 CURD 页面,根据接口文档生成 TS 类型,生成 ...