CF510D Fox And Jumping
题目
见链接。
题解
方法一
知识点:背包dp,STL。
题目意思是让我们判断能否从这些数中选出一些数使得选的数公共gcd为 \(1\),如果可以输出最小花费。
一眼背包dp,但要map超大背包优化,因为数字很大 \(10^9\) ,显然无法直接在第二维放下。
设 \(dp[i][j]\) 表示考虑到第 \(i\) 个数,所有数的gcd为 \(j\) 的情况。 因为用map优化了,没法填表了,因为每行都的元素都不确定,应该用刷表法,用这一行结果推到下一行。转移方程为:
\[\begin{aligned}
dp[i][x] &= \min(dp[i-1][x],dp[i][x])\\
dp[i][gcd(l[i],x)] &= \min(dp[i-1][x] + c[i],dp[i][gcd(l[i],x)])
\end{aligned}
\]
前者表示不选第 \(i\) 个数的转移,后者表示选了第 \(i\) 个数的转移。
因为不能 memset
初始化正无穷,因此要做到循环中对第一次出现的状态初始化。同时,这也是解决数组太大每次全部初始化很费时间情况下的一种技巧。
注意为了方便,初始化了 \(dp[0][0] = 0\) 。用于每次选一个数自己是第一个数的情况,能正确被标记而不用特判,即 \(gcd(l[i],0) = l[i]\)。因为在我们写的gcd函数里, \(0\) 可以当作特殊的单位元,特殊之处在于其应该出现在函数的后面一个位置,即 \(gcd(a,0)\) ,否则会出错。
时间复杂度 \(O(n*玄学)\) ,qwq最大公约数种类不知道怎么算
空间复杂度 \(O(玄学)\)
方法二
知识点:BFS,优先队列,记忆化搜索。
说白了就是记忆化搜索,用广搜做最短路,求花费最小的路径,因此用优先队列。细节处理比dp少一点,而且差不多。
时间复杂度 \(O(能过而且更快)\)
空间复杂度 \(O(能过)\)
代码
方法一
///满足最优子结构,顺序上没有后效性可以dp
#include <bits/stdc++.h>
using namespace std;
int l[307], c[307];
unordered_map<int, int> dp[307];
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> l[i];
for (int i = 1;i <= n;i++) cin >> c[i];
dp[0][0] = 0;///gcd单位元
for (int i = 1;i <= n;i++) {
for (auto [x, y] : dp[i - 1]) {///向后传递,不能写找前继承
if (!dp[i].count(x)) dp[i][x] = 0x3f3f3f3f;
dp[i][x] = min(dp[i][x], y);
int d = gcd(l[i], x);
if (!dp[i].count(d)) dp[i][d] = 0x3f3f3f3f;
dp[i][d] = min(dp[i][d], y + c[i]);
}
}
if (dp[n].count(1)) cout << dp[n][1] << '\n';
else cout << -1 << '\n';
return 0;
}
方法二
///也可以写成优先队列bfs,好处是不用考虑后效性,以花费从小到大为顺序扩展,复杂度玄学
#include <bits/stdc++.h>
using namespace std;
int n;
int l[307], c[307];
unordered_set<int> vis;
struct node {
int l;
int c;
friend bool operator<(const node &a, const node &b) {
return a.c > b.c;
}
};
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int bfs() {
priority_queue<node> pq;
pq.push({ 0,0 });
while (!pq.empty()) {
node cur = pq.top();
pq.pop();
if (cur.l == 1) return cur.c;
if (vis.count(cur.l)) continue;
vis.insert(cur.l);///经过后再锁
for (int i = 1;i <= n;i++) {
int d = gcd(l[i], cur.l);
if (vis.count(d)) continue;
pq.push({ d,cur.c + c[i] });
}
}
return -1;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1;i <= n;i++) cin >> l[i];
for (int i = 1;i <= n;i++) cin >> c[i];
cout << bfs() << '\n';
return 0;
}
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16581767.html