关于此题[ABC350E] Toward 0和[ABC188F] +1-1x2记忆化搜索的一些总结

传送门1
传送门2
这两道题都有个特性,那就是数据范围到了1018,这会让我们想用记忆化搜索或者期望DP的想法望而却退
但是实际上我们可以用map。有人会说,用map那时间上貌似也过不去啊!但是我们发现这两道题当中,我们可以进行的操作都有除法操作,这就有点像势能线段树,时间复杂度实际上是log级别的。

对于第一题期望DP,我们显然是DP不了了,因为没法一个个转移,只能记忆化搜索,假设dp[u]表示从u到0的期望最小花费,那么此时我们有两条路,一条是选第一种操作方法,即花费X元将u变成ua,另一种即通过掷骰子的方法。第二种操作需要推一下式子,即:

dp[u]=16(dp[u]+dp[u2]+dp[u3]+dp[u4]+dp[u5]+dp[u6])+Y

我们发现这个式子有后效性,那么就可以高斯消元(我也不会这题也没必要,或者移项即可:

dp[u]=15(dp[u2]+dp[u3]+dp[u4]+dp[u5]+dp[u6])+65Y

于是这道题就被解决了。
代码:

#include<bits/stdc++.h>

using namespace std;

long long t;
const long long N = 2e5 + 10;
long long n,a,x,y;
map<long long,long double> dp;

long double dfs(long long m) {
    if(m == 0) return 0;
    if(dp[m]) return dp[m];
    dp[m] = dfs(m/a) + x;
    dp[m] = min(dp[m],1.0l / 5.0l * (dfs(m/2) + dfs(m/3) + dfs(m/4) + dfs(m/5) + dfs(m/6)) + 6.0l / 5.0l * (double)y);
    return dp[m];
}

void solve() {
    cin >> n >> a >> x >> y;
    cout << fixed << setprecision(7) << dfs(n);
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();

    return 0;
}

对于第二道题:
同样的,我们设dp[u]表示从x到u需要的最小操作次数进行记忆化搜索,那么对于三个操作,我们发现:

  1. 如果此时u<x,那么x只能一个一个减到达u,答案也就是x-u

  2. 如果此时u>x,那么有一个显而易见的答案u-x,但是由于还有乘法操作,对于u我们再分类讨论:

  • 如果此时u是2的倍数,那么u可以从u2来,所以此时用来更新答案的就是dp[u2]+1
  • 如果此时u不是2的倍数,那么u可以从u+12或者u12经过两次操作而来,此时用来更新答案的就是min(dp[u+12],dp[u12])+2

而至于为什么不从x出发通过乘2来到y(因为我一开始就是这么做的),这样会TLE
代码:

#include<bits/stdc++.h>

using namespace std;

long long t;
long long x,yy;
map<long long,long long> dp;

long long dfs(long long y) {
    if(x == y) return 0;
    if(dp[y]) return dp[y];
    if(x < y) {
        dp[y] = y - x;
        if(y % 2 == 0)
            dp[y] = min(dp[y],dfs(y/2) + 1);
        else
            dp[y] = min(dp[y],min(dfs((y + 1) / 2) + 2,dfs((y - 1) / 2) + 2));
    }
    else dp[y] = x - y;
    return dp[y];
}

void solve() {
    cin >> x >> yy;
    cout << dfs(yy);
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();

    return 0;
}

2025.1.12更新:
再来一道(以后要是碰见一样的还不会做我吃了,ABC300 E - Dice Product 3
转移的方程和第一题一样,只不过少了个Y,因为这道题中没有花费

dp[u]=15(dp[u2]+dp[u3]+dp[u4]+dp[u5]+dp[u6])

题目要求分数的模,由递推式可知分数的分母是5,于是求一下逆元即可
代码:

#include<bits/stdc++.h>

using namespace std;

const long long mod = 998244353;
long long t;
long long n,tmp;
map<long long,long long> dp;

long long quickMul(long long x,long long k) {
    if(k == 0) return 1;
    long long tp = quickMul(x,k/2);
    tp = (tp * tp) % mod;
    if(k % 2) tp = (tp * x) % mod;
    return tp;
}

long long dfs(long long u) {
    if(u == 1) return 1;
    if(dp[u]) return dp[u];
    long long res = 0;
    if(u % 2 == 0) res += dfs(u/2);
    if(u % 3 == 0) res += dfs(u/3);
    if(u % 4 == 0) res += dfs(u/4);
    if(u % 5 == 0) res += dfs(u/5);
    if(u % 6 == 0) res += dfs(u/6);
    dp[u] = ((res % mod) * (tmp % mod)) % mod;
    return ((res % mod) * (tmp % mod)) % mod;
}

void solve() {
    cin >> n;
    tmp = quickMul(5,mod-2);
    cout << dfs(n);
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();

    return 0;
}
posted @   孤枕  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示