HDU4028 The time of a day (离散化dp)

本文探讨了一个关于时钟指针的数学问题,使用动态规划的方法来解决选择某些时钟指针使得其最小公倍数大于给定值的问题。通过构建动态规划表,避免了重复计算,有效提高了算法效率。

题目大意

一个时钟,有n(0<n<40)个时钟指针,对于编号为i的指针每隔i秒会返回原位置,规定你所选择的一些个时钟指针从初始位置开始移动回到原来的位置的时间长度为一天,给定一个值m,问有多少种选择方案能使一天的时间大于m(1 <= M <= 2 63-1)。
实际上就是求这1~n个数中,选择的不同数的最小公倍数能够大于m的方案数量。暴力肯定会超时。
虽然选择方案数量很大但是,对于各种方案所组成的最小公倍数却比较少,会存在很多重复的,所以可以用动态规划。
dp[i][j]代表对于前i个数字组成的最小公倍数为j的共有dp[i][j]种方案,那么:

dp[i][j]=dp[i-1][j](不选第i个)+dp[i-1][k](选第i个,其中lcm(k,i)=j)

#include <iostream>
#include <vector>
#include <map>
#include <iterator>
using namespace std;

typedef long long ll;
typedef map<ll, ll> mp;//first表示最小公倍数,second表示有多少种情况。
const int MAX_N = 45;

vector<mp> dp(MAX_N);//dp[i][j] = x;表示n为i时,可以有it->second(即x)种情况组成it->first(即j)

ll gcd(ll a, ll b) {//求最大公约数
    return b ? gcd(b, a % b) : a;
}

ll lcm(ll a, ll b) {//求最小公倍数
    return a * b / gcd(a, b);
}

void init() {
    dp[1][1] = 1;
    for (int i = 2; i < MAX_N; ++i) {//表示有i个指针的时候
        dp[i] = dp[i - 1];//不取第i个的所有情况
        ++dp[i][i];//只取第i个的情况
        for (mp::iterator it = dp[i - 1].begin(); it != dp[i - 1].end(); ++it)
            dp[i][lcm(i, it->first)] += it->second;//在前i - 1的基础上加上第i个数
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    init();
    int t;
    cin >> t;
    for (int ks = 1; ks <= t; ++ks) {
        ll n, m;
        cin >> n >> m;
        ll ans = 0;
        for (mp::iterator it = dp[n].begin(); it != dp[n].end(); ++it) {//计算满足条件的个数
            if (it->first >= m) ans += it->second;
        }
        cout << "Case #" << ks << ": " << ans << endl;
    }
    return 0;
}
这里借用了作者:sugar_coated的代码,我觉得这个注释写的很清楚了
链接:https://www.jianshu.com/p/6b3ac17d63c2
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值