题目:点击打开链接
题目大意:
一个时钟,有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
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
本文探讨了一个关于时钟指针的数学问题,使用动态规划的方法来解决选择某些时钟指针使得其最小公倍数大于给定值的问题。通过构建动态规划表,避免了重复计算,有效提高了算法效率。
3981

被折叠的 条评论
为什么被折叠?



