【题目链接】
https://nanti.jisuanke.com/t/42403
【前言】
不得不说这真的是一道毒瘤题,算法众多而且难想,不看题解想不出来系列
【题目大意】
有n个点,每个点都有能量ai,你的初始能量为a0,从0出发,每次你只能到达能量低于你自身能量的点,问你有多少种路径可以经过所有点
数据范围:
1 <= n <= 100000
0 <= ai <= 50
【解题思路】
一、优先暴力
首先很容易想到暴力dfs,可以设置三个参数,分别表示当前位置,当前剩余能量,以及剩余未路过的点(实际上当前位置可以忽略),遍历数组a,找到所有可以达到的点递归处理,然后考虑剪枝,我们可以将数组a排序,如果我们此时的rest < ai,那么显然后面的点均无法到达,直接return,最后加上各种卡常优化
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int mod = 1e9 + 7;
int a[maxn];
bool vis[maxn];
int n, ans = 0;
inline void dfs(register int pos, register int rest, register int cnt) {
if (cnt == n) {
ans = (ans + 1) % mod;
return;
}
for (register int i = 1; i <= n; ++i) {
if (vis[i]) continue;
if(rest < a[i]) return;
vis[i] = true;
dfs(i, rest + a[i], cnt + 1);
vis[i] = false;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (register int i = 0; i <= n; ++i) cin >> a[i];
sort(a + 1, a + n + 1);
cout << dfs(a[0], 0) << endl;
}
但是时间复杂度为O(n2)显然会超时
二、记忆化
既然dfs超时,我们考虑能不能记忆化以减少重复计算,由于数据过大,不能使用二维数组,考虑用map + pair进行记忆化
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
const int maxn = 100010;
const int mod = 1e9 + 7;
int a[maxn];
bool vis[maxn];
int n;
map<pair<int, int>, ll> dp; //记录每种情况的搜索结果
map<pair<int, int>, bool> ed; //记录此种情况是否被搜索过
inline ll dfs(register int rest, register int cnt)
{
if (cnt == n) return 1;
register pair<int, int> pp(rest, cnt);
register ll& x = dp[pp];
register bool& y