洛谷P2967题解

明显的动态规划

题目大意

\(n\) 种游戏主机,第 \(i\) 种主机的价格是 \(P_i\)。该主机有 \(G_i\) 个独占游戏。很明显,奶牛必须先买进一种游戏主机,才能买进在这种主机上运行的游戏。在每种主机中,游戏 \(j\) 的价格为 \(\mathit{GP}_j\),每头奶牛在玩了该游戏后的牛奶产量为 \(\mathit{PV}_j\)。预算为 \(V\)。请帮怎么购买,产出值的和最大。

分析

给定空间的最大价值,明显的背包啊!

本蒟蒻是不可能第一遍就打正解的。

已下是P2967的1.0版本

\(f_{i,j,v}\) 表示现在第 \(i\) 个平台,第 \(j\) 个游戏,现有预算为 \(v\) 的情况下的最大产量。

\[f_{i,j,v}=\max(f_{i,j-1,v},f_{i,j-1,v-\mathit{GP}_{ij}}+\mathit{PV}_{ij}) \]

到现在为止这个方程不完整,只对后两位进行了递推,\(i\) 没有递推过,所以还要加上。

\(f_{i,0,v}\) 表示确定要用第 \(i\) 个主机

其实这个方程里面有一个隐含条件就是我们会用第 \(i\) 个主机。这样的话我们还需要考虑分给第 \(i\) 个主机多少钱。所以要再修改一下。好麻烦

已下是P2967的2.0版本

\(g_{i,v}\) 考虑前 \(i\) 个平台最大收益。

\[g_{i,v}=\max_{0\leqslant k\leqslant v}h_{i,k}+g_{i-1,v-k} \]

其中 \(h_{i,k}=f_{i,G_i,k-P_i }\)

程序如下:

#include<iostream>
#include<algorithm>
const int maxv = 100001;
int g[maxv], f[maxv];
int main()
{
  int n, v;
  std::cin >> n >> v;
  while (n --> 0) {
  	int p, game;
  	std::cin >> p >> game;  	
  	for (int x = 0; x <= v; ++x)
  	  f[x] = 0;
  	while (game --> 0) {
  	  int pv, gp;
  	  std::cin >> gp >> pv;
  	  for (int x = v; x >= gp; x--)
  	    f[x] = std::max(f[x], f[x-gp] + pv);  	    
  	}
  	for (int x = v; x >= p; x--)
  	  for (int k = x-p; k >= 0; k--)
  	    g[x] = std::max(g[x], g[x-p-k] + f[k]);
  }
  std::cout << g[v];
}

跑了一下,39分。

再改!

一下是P2967的3.0正解版本

要想做好动态规划,枚举是王道。大家可以试一试,枚举一个主机,再抉择是否买第二个主机。
为了方便枚举,可以把主机的价格设为0。

假设第一个主机当中:

价格 产出
0 0
10 30
15 20
25 50

假设第二个主机价格为100元。在我们抉择是否买第二个主机的时候,可以在这个表上面修稿。如果不买,那么保存副本,如果买,就直接修改。怎么改呢?看好了!

我们把主机的价格加到表格中

价格 产出
100 0
110 30
115 20
125 50
这样没错吧!
再将表翻倍。
如果有三种方案的话就翻三翻。

4-->8-->16-->32

那到底怎么决定买不买呢?

我们把所有的方案合并。有人会说,那会不会超时呢?
其实不会的。就算你有100000种方案,合并也只用100000次,也没什么了不起的。所以500个游戏,每个最多100000种方案,也就500000000次,不会超的。

就这样翻倍,合并,剪枝,就能比较快的决定出需不需要了。

这样讲的有些抽象,具体看看代码吧!

代码

没想到吧,只有24行!

#include<iostream>
#include<algorithm>
const int maxv = 100001;
int save[maxv], fork[maxv];
int main()
{
  int n, v;
  std::cin >> n >> v;
  while (n --> 0) {
  	int p, g;
  	std::cin >> p >> g; 
  	for (int x = 0; x+p <= v; ++x)	// fork 在 [0, p) 之间是无效的	  	
  	  fork[x+p] = save[x];  	  
  	while (g --> 0) {
  	  int pv, gp;
  	  std::cin >> gp >> pv;
  	  for (int x = v-gp; x >= p; x--) 
  	    fork[x+gp] = std::max(fork[x+gp], fork[x] + pv);  	    
  	}
  	for (int x = v; x >= p; x--)
  	  save[x] = std::max(save[x], fork[x]); 
  }
  std::cout << save[v];
}

AC图片

posted @ 2021-08-02 12:06  世外第一人  阅读(58)  评论(0)    收藏  举报