您的位置:首页 > 其它

随时记录灵感——DP相关

2016-05-14 00:20 211 查看
今天下午记录的灵感因为没有保存所以消失了QAQ

那就重写一遍吧。

首先题目是这样的:

有n个项目,每个项目都有m种东西可选,每个项目必须选一个东西。

对于某个项目i,第j种东西:

有其性能wijw_{ij}和花费cijc_{ij}

现在求

max(min(wij)∑cij)max(\frac {min(w_ {ij})}{\sum c_{ij}})

设dp[i][j]表示到第i个选到的性能为j目前在考虑选不选第k个东西的最小花费。

dp[i][min(j,w[i][k])=min(dp[i−1][j]+c[i][k],dp[i][min(j,w[i][k]))dp[i][min(j,w[i][k]) = min(dp[i-1][j] + c[i][k] , dp[i][min(j,w[i][k]))

嗯……然后最后扫一遍dp
[j],每个j都除以dp
[j]取最大的那个即可。

忽然感觉这好像一个暴力啊……

像不像是枚举?

枚举每个项目,枚举每个东西,枚举每个东西能到达的结果。

我们可以考虑这种东西:

f[i][k][p]表示在第i个项目,目前最小的带宽为k,花费为p可不可以到达。

那么,显然:

f[0][0][0][0] = 1;

然后转移的话:

f[i][k][p]=f[i−1][s>=k][p−cij] |f[i−1][k][p]; f[i][k][p] = f[i - 1][s >= k][p - c_{ij}]\ \ \ | f[i - 1][k][p];\ \ \

我们发现这种转移简直暴力到了一定地步,所以我们感觉……

现在我写的DP就是一个枚举

嗯……可是你怎么保证你的DP不是枚举呢?

我的这个转移,可以说,不是一个DP

但是也是一个DP啊,因为它有状态,有转移,没有后效性。

问题在于:

原问题有一个比较优美的性质,就是在于:

你不必保存所有的花费可不可以到达,因为你只在乎那个最小的花费可不可以到达。

那么我们把这个开个int数组,然后省去一维,降一下空间就好了。

同样的,我们的时间也减少了一个n。

也就是说,我们根据问题的性质,成功降了一下维。

所以这就叫DP了?

我觉得这就是DP了吧。

我仔细回忆一下,可以发现几个经典模型:

01背包:

最蠢的暴力枚举:

f[i][j][k]表示第i个物品,容量为j,价值为k是可不可达的。

f[i][j][k] = f[i - 1][j - w][k - c] | f[i - 1][j][k]

最后暴力扫一遍每个状态即可。

我们发现这样的复杂度是(n ^ 3),但是比枚举每一个物品选或者不选不知道要高贵到哪里去了。

思路:

暴力枚举每一个物品?O(2n2 ^ n)

- > 影响最终答案,和最终答案有关的是什么?选择哪个物品,选还是不选,容量是怎样的,价值是怎样的。那么,我们直接有上述转移:f[i][j][k] = f[i - 1][j - w][k - c] | f[i - 1][j][k],即我们考虑最后扫一遍对于每个状态可不可达,取一下最大值就行了。O(n3n ^ 3)

- >可是再仔细一想:我们只需要考虑每个容量记录一个最优的价值就好了,为什么记录比它还差的呢?没错,这样做的最后转移就是我们熟悉的: f[i][j]=max(f[i−1][j−w[i]]+c[i],f[i−1][j]);f[i][j] = max(f[i - 1][j - w[i]] + c[i],f[i - 1][j]);

所以,此时并不关心你这个状态可不可达,只关心在这个状态可达的情况下,最优的结局是怎样的

即:最优子结构

并且,由于问题的特殊性,我们显然能知道,当前状态的最优结局,我们在后面也会用到这一状态的最优结局。并且,我们当前这一步的状态对于后面的任何抉择来讲都是没有影响的

即:无后效性

其实,当时我表示不是很理解:

在01背包中,这个东西被选了,之后的选的就少了啊,这怎么可能对后来的状态没有影响呢?

现在我好像明白了:

这种东西有没有对后来状态的影响,得根据当前状态的定义来判断。

比如对于01背包,它选了这件物品,和不选这件物品,对于状态定义的不同显然结局不太一样:

假设:f[i]表示强制选择这个物品的最大价值。

显然这个东西是没办法与后面产生什么关系的。

因为f[i]和任意的一个f[j]都没有任何联系,它们不仅是互相影响的而且是没有压缩整个问题的。

而现在:

f[i][j] 表示对于第i个物品,还剩j容量的背包能取得的最大价值。

显然这一点跟前面取了什么是无关的,这个状态和什么有关?

显然这个状态跟前面某个东西取或者没有取是无关的,它只跟前面的背包容量有关,甚至和前i - 1个物品都没有任何关系。

再重复一遍当前状态:第i个物品背包容量为j的最大价值只与两个状态有关:

1.当前这个物品不选,前i - 1个物品,背包容量为j的最大价值。

2.当前这个物品选了,前i - 1个物品,背包容量为j - w[i]的最大价值。

只跟这两个状态有关,与

选了什么到达了这些状态

无关。

真的写的有点累了呢……

我来冷静一下:

我们来看一道经典的LIS(最长不上升子序列)问题

暴力枚举每个元素就不说了。

现在说说暴力枚举每个状态:

设f[i][j][k]表示:前i个元素,长度为j可不可以通过大小为k的元素到达。

那么问题分为两部分:

选第i个,还是不选呢?

如果不选,那么f[i][j][k]= f[i - 1][j][k]。

如果选呢?f[i][j][k] = f[i - 1][j - 1][s >= k];

所以最终结果是:

f[i][j][k] = f[i - 1][j][k] | (f[i - 1][j - 1][s >= k] && a[i] == k);f[i][j][k] = f[i - 1][j][k] | (f[i - 1][j - 1][s >= k] && a[i] == k);

这样问题就解决了!复杂度O(n3∗Wn^3 * W)

是不是非常蠢呢?

嗯……

我们考虑优化这个dp,发现我们并不用关心大小为k抑或是不是k,我们只关心前面的出现的那些元素。

设f[i][j][k]表示:前i个元素,长度为j可不可以通过第k大的元素到达。

f[i][j][k] = f[i - 1][j][k]\ \ \ | (f[i - 1][j - 1][s >= k] && a[i]排在第k大)f[i][j][k] = f[i - 1][j][k]\ \ \ | (f[i - 1][j - 1][s >= k] && a[i]排在第k大)

复杂度O(n4n ^ 4)。

等等这好像还是有点蠢对吧……

因为你的第三维有n - 1个无用的状态啊。

我们考虑这样做:

f[i][j][0]表示对于前i个长度为j可不可以通过不选i到达。

f[i][j][1]表示对于前i个长度为j可不可以通过选i到达。

f[i][j][0]=f[i−1][j][0] |f[i−1][j][1]f[i][j][0] = f[i - 1][j][0] \ \ \ | f[i - 1][j][1]

f[i][j][1] = f[i - 1][j][0] | f[k][j - 1][1] (k < i && a[k] > a[i])f[i][j][1] = f[i - 1][j][0] | f[k][j - 1][1] (k < i && a[k] > a[i])

其实这样已经差不多了,我们发现复杂度是:O(n3n ^ 3)

然而……

问题真的这样结束了吗?

我们考虑到一点,就是长度那一维应该是不用记的,因为我们同样只关心这一点能造成的最优长度,不用关心比它差的结果。

于是我们:

f[i][0]=max(f[i−1][0],f[i−1][1])f[i][0] = max(f[i - 1][0],f[i - 1][1])

f[i][1] = max(f[i - 1][0],f[j][1](a[j] >= a[i] && j < i))f[i][1] = max(f[i - 1][0],f[j][1](a[j] >= a[i] && j < i))

然后我们发现其实……

f[i][0]就是打酱油的,因为它什么都派不上用场。

我们直接记录强制选择i所能达到的最大长度其实就解决了。

嗯实际上最终的复杂度是可以做到O(nlog2nnlog_2n)的,用树状数组优化即可。

这个灵感应该还是蛮重要的。

至少DP其实算是暴力枚举每一个状态。

完结撒花。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: