1 条题解
-
1
守护神兽的修炼计划II
tag
数论,枚举,图论?
题意简述
给定 个数 ,每次可以选择一个数 ,令 ,或者令 。求令所有数相等的最小操作次数。
题解思路
分析题意,我们考虑将所有的数变成目标数 , 应该形如:,其中 是所有数的公共可达点。公共可达点指的是,所有数都可以通过若干次除以2的操作变成 。我们这里只考虑通过除以2的操作变成 ,因为乘以2的操作形成的公共可达点可以通过枚举 中的 来得到,他们的 本质相同,而只有除以二存在向下取整的操作,可能会出现新的 。可以使用map容器来记录所有数的可达点权值,每次出现一个数就+1,最后权值为n的数就是所有数的公共可达点。最后,我们枚举所有的 的 的整数次幂的倍数,作为目标数,然后依次计算即可。
时间复杂度分析:我们枚举所有的数,不断除2直到为 ,得到每个数的可达点,时间复杂度是 。同时,需要注意的,公共可达点的数的数量不会超过 个(估计的,无证明)。随后枚举所有的公共可达点(即 )的 的整数次幂的倍数,时间复杂度是 。随后每次计算每个数变成目标数 的操作次数,时间复杂度是 。所以总时间复杂度是 。约等于 。
参考代码
signed main() { IOS; int n; cin >> n; vector<int> a(n + 1); map<int, int> mp; // 记录可达点的权值 for(int i = 1; i <= n; i ++) { cin >> a[i]; int t = a[i]; int c = 0; while(t) // 枚举所有可达点 { mp[t]++; t >>= 1; } } // 计算 u 以 base 为可达点,变成 x 的操作次数 auto calc = [&](int u,int x, int base) -> int { int t = 0; while(u > base) { // 如果 base 是 u 的一个可达点,那么 u 可以直接除2变成 x if(u == x) return t; t ++; u /= 2; } // 否则先变成 base,再变成 x while(base < x) { t ++; base *= 2; } return t; }; i64 ans = 1e18; for(auto [i, c] : mp) { // 只有权值为 n 的可达点才会被枚举 if(c < n) continue; i64 t = 0; // 枚举他的所有2的整数次幂的倍数 for(int k = i; k <= 1e5; k *= 2) { t = 0; for(int j = 1; j <= n; j ++) { t += calc(a[j], k, i); } ans = min(ans, t); } } cout << ans << '\n'; return 0; }
- 1
信息
- ID
- 226
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 30
- 已通过
- 3
- 上传者