longdie探秘龙之谷(NP完全问题)-题解
longdie
2020-12-31 14:12:30
应该都能看出来这是一个01背包问题吧。
## 前言
首先提前说明01背包问题是一个NP完全问题。
证明:
如果你采用搜索那么复杂度是非常显然的 $O(2^n)$。
当然可以用DP,复杂度为 $O(nm)$,看起来不像NP完全问题,但是m的大小随着n的增加快速递增,接近指数级别。
所以它是一个NP完全问题。
## 前 30% 的数据:
这个就是一个裸的 01 背包,不用讲了吧。
## 对与 n <= 40 的数据:
不难想到**双向搜索**,然后二分一下就可以了。
## ?分(乱搞)
对于longdie出的题只能乱搞了啊。
我们考虑部分背包问题的做法,可以设价值比为 $v_i/w_i$,那么显然价值比越大越应该取,所以按照价值比拍个序。
但是放到这道题里是显然不对的,随便可以举出反例,但是我们可以采用一些随机化的方法:
1.排序后从每个点开始都进行一次贪心。
这样肯定会增大正确性,但是它也不是对的,但是事实证明这样可以把误差降低到: $1/n*ans$,对于一些数据已经可以水过了。
但是这样显然基本上不能拿分。
2. 设一个函数 $f(x)$ 表示不取这个物品的概率,那么显然对于价值比越大的龙魂它的 $f(x)$ 应该越小,所以我们不妨设 $f(x) = n-i+2 $,具体操作可以这样:
若 $rand()$ % $f(x) == 0$ ,那我就不取,否则取,我们可以做很多次,会使正确性大大提升。
预估得分:0-100分
这样加上前面非常的部分分就可以拿到非常高的分数了。
## 对于满分做法
继续考虑乱搞,我们可以从复杂度最高的搜索入手,显然搜索的复杂度是 $O(2^n)$。
但是我们可以考虑 $A^*$ 做法。
具体的我们设 $nw$ 为当前已经用的体积,$nv$ 为当前已经用的价值。
那么我们设 $f[num][nw]$ 表示从第 $num+1$ 个物品开始,当前已经用了 $nw$ 的体积,期望能获得的最大价值。
显然这个期望价值只能大不能小,所以我们可以直接把这个表示成部分背包的价值,这样一定比实际上的大。
这个算法的期望复杂度是 $O(k * 2^n)$,其中 k 是一个 大于0小于1的常数。
实际上这个算法在 n<=1000 的时候很难被卡,所以可以当作正解来做。
下面附上一份本人的代码:
```cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 4005;
inline int read(int x = 0) { return scanf("%d", &x), x; }
int m, n, ans;
struct node {
int w, v; double wv;
} a[N];
inline bool cmp (node x, node y) {
return x.wv > y.wv || (x.wv == y.wv && x.v > y.v);
}
int func(int num, int sw) {
register int res = 0;
for(register int i = num + 1; i <= n; i ++) {
if(sw + a[i].w <= m) {
sw += a[i].w, res += a[i].v;
}
else return res + (m - sw) * a[i].wv;
}
return res;
}
void dfs(int num, int nw, int nv) {
if(nv > ans) ans = nv;
if(num == n + 1) return;
if(nw + a[num].w <= m) dfs(num + 1, nw + a[num].w, nv + a[num].v);
if(func(num, nw) + nv > ans) dfs(num + 1, nw, nv);
}
int main() {
n = read(), m = read();
for(register int i = 1; i <= n; ++i) {
a[i].w = read(), a[i].v = read(), a[i].wv = 1.0 * a[i].v / a[i].w;
}
sort(a + 1, a + n + 1, cmp);
dfs(1, 0, 0);
cout << ans << '\n';
return 0;
}
```
当然如果你不写 $A^*$ 而是大力剪枝的话可能也可以过掉,但是我可以增大数据范围或者构造特殊数据,但是 $A^*$ 算法很难被卡。
当然可能某些大佬会更加高级的算法比如凸包优化,这个我表示我不会。
但是凸包优化的复杂度也是很高的。
所以对于NP完全问题,很难有非常优秀的解法。