可知题意是让求一定操作数内要能得到的数末尾的0最多是几
根据数的唯一分解定理,末尾为0的个数只取决于2和5的数量,因为最多18个0,也就是最多18对2和5,然后得到对应的操作次数即可
AC代码:
/*########################################################## # File Name: 2.cpp # Author: HideInTheSea # Created Time: 2022年11月20日 星期日 19时24分47秒 ##########################################################*/ #include using namespace std; using LL = long long; void Solve() { LL n, m; cin >> n >> m; for (int i = 18; i >= 0; i--) { LL x = n; int cnt2 = i, cnt5 = i; while (x % 2 == 0) { x /= 2; cnt2--; } while (x % 5 == 0) { x /= 5; cnt5--; } LL y = 1; for (int j = 0; j < cnt2; j++) { y *= 2; } for (int j = 0; j < cnt5; j++) { y *= 5; } if (y <= m) { LL k = m / y * y; cout << n * k << '\n'; return; } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { Solve(); } return 0; }挺不错的DP题
主角有两瓶绿色药水和一瓶蓝色药水
主角每秒可以执行三种操作:
1.主角吸收随从能量的一半
2.主角喝绿色药水能量乘2
3.主角喝蓝色药水能量乘3
问最多吸收多少随从
首先贪心的去想随从能量越小,越要放在前面,这样最容易吸收他的能量而不浪费药水,然后再考虑DP
状态边界为dp[0][i][j],分别为喝了i瓶绿色药水和喝了j瓶蓝色药水
dp[i][j][k]表示到达第i秒用了j瓶绿色药水和k瓶蓝色药水时最大的能量值和吸收随从的次数,转移就是分别模拟三种操作
AC代码:
/*########################################################## # File Name: 1.cpp # Author: HideInTheSea # Created Time: 2022年11月22日 星期二 18时06分06秒 ##########################################################*/ #include using namespace std; using LL = long long; void Solve() { int n; LL h; cin >> n >> h; vector<int> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } sort(a.begin() + 1, a.end()); vectorint>>>> dp(n + 1, vector int>>> (3, vector int>> (2))); for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { dp[0][i][j].first = h * (i == 0 ? 1 : 2) * (j == 0 ? 1 : 3) * (i == 2 ? 2 : 1); dp[0][i][j].second = 0; } } for (int i = 1; i <= n; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 2; k++) { dp[i][j][k] = make_pair(dp[i - 1][j][k].first + (dp[i - 1][j][k].first > a[i]) * a[i] / 2, dp[i - 1][j][k].second + (dp[i - 1][j][k].first > a[i])); } } dp[i][0][1] = max(dp[i][0][1], pairint> {dp[i][0][0].first * 3, dp[i][0][0].second}); dp[i][1][1] = max({dp[i][1][1], pairint> {dp[i][1][0].first * 3, dp[i][1][0].second}, pair int> {dp[i][0][1].first * 2, dp[i][0][1].second}}); dp[i][1][0] = max(dp[i][1][0], pairint> {dp[i][0][0].first * 2, dp[i][0][0].second}); dp[i][2][1] = max({dp[i][2][1], pairint> {dp[i][2][0].first * 3, dp[i][2][0].second}, pair int> {dp[i][1][1].first * 2, dp[i][1][1].second}}); dp[i][2][0] = max(dp[i][2][0], pairint> {dp[i][1][0].first * 2, dp[i][1][0].second}); } cout << dp[n][2][1].second << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { Solve(); } return 0; }首先要看清题干,给出一段在p进制下的数,虽然每次操作可以选择任意一位数,但最后写在黑板上的是x+1,也就是说每次操作只能最低位+1
那么首先想到的是进行p-1次操作一定能完成任务,但操作数太多了,题目要最小化
其次想到最低位是否要进位?
如果不进位,前提是除最低位以外的其他位要全部包含0到(最低位-1)的所有数,否则必须通过进位来取到不包含的数,然后在考虑除最低位以外的数在(最低位+1)到(p-1)中最大的不存在的数,这是计算的边界,因为n<=100,所以不需要考虑时间问题,暴力即可
如果进位,那么先把要进位的数按运算法则运算,得到最终的进位后的数,如果都是(p-1),进位后会在最高位前面多出一个1,运算的时候也要存下出现过的数,最后就是最低位置为0,然后暴力查找最大的没表示到的数,暴力查找时,因为大于等于最低位的数都在进位时遍历过了,所以直接从(最低位-1)开始暴力的遍历即可
AC代码:
/*########################################################## # File Name: 1.cpp # Author: HideInTheSea # Created Time: 2022年11月23日 星期三 18时43分30秒 ##########################################################*/ #include using namespace std; using LL = long long; void Solve() { int n, p; cin >> n >> p; vector<int> a(n + 1); map<int, int> mp; for (int i = 1; i <= n; i++) { cin >> a[i]; mp[a[i]] = 1; } if (mp.size() == p) { cout << "0\n"; return; } function<bool(int, int)> check = [&](int x, int y) { for (int i = x; i <= y; i++) { if (mp.count(i) == 0) { return false; } } return true; }; function<int(int, int)> check1 = [&](int x, int y) { for (int i = y; i > x; i--) { if (mp.count(i) == 0) { return i; } } return x; }; int maxx = check1(a[n] + 1, p - 1); if (check(0, a[n])) { if (maxx == a[n]) { cout << p - a[n] - 1 << '\n'; } else { cout << maxx - a[n] << '\n'; } } else { LL ans = p - a[n]; int x = a[n]; a[n] = 0; mp[a[n]] = 1; for (int i = n - 1; i >= 0; i--) { a[i]++; if (a[i] == p) { a[i] = 0; } else { mp[a[i]] = 1; break; } } function<int(int, int)> calc = [&](int x, int y) { for (int i = y - 1; i >= x; i--) { if (mp.count(i) == 0) { return i; } } return 0; }; cout << ans + calc(0, x) << '\n'; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { Solve(); } return 0; }看到题,首先考虑贪心的把越小的数越要放到前面,然后根据题目的描述得知,给出的数组中的数是相邻两个数之间最大的数,因此要把这个数放到第二个位置,这样才能保证字典序最小
根据样例能够看出,如果一个数出现了两次,或者出现的无论如何都没法组成全排列的话就输出-1
再进行构造答案,首先根据字典序,标号小的一定要往前放,再考虑b中的数是相邻两个数之间最大的数,那么要尽可能地让他俩之差小,这样标号小地数才能够尽可能的往前放,在遍历的时候应该从标号大的数递减遍历,这样能够让没在b中出现的相对比较大的数放在靠后的位置,如果一个数在b中出现过,就把该数下标放到堆里,这样既能保证b中的数是相邻两个数之间最大的,也能保证两个数之差最小,这样才能使得最后的全排列字典序最小
AC代码:
/*########################################################## # File Name: 2.cpp # Author: HideInTheSea # Created Time: 2022年11月22日 星期二 20时43分24秒 ##########################################################*/ #include using namespace std; using LL = long long; void Solve() { int n; cin >> n; vector<int> a(n + 1), b(n + 1); bool ok = true; for (int i = 2; i <= n; i += 2) { cin >> a[i]; b[a[i]] = i; } priority_queue<int> q; for (int i = n; i >= 1; i--) { if (!b[i]) { if (q.empty()) { ok = false; break; } else { a[q.top() - 1] = i; q.pop(); } } else { q.push(b[i]); } } if (!ok) { cout << "-1\n"; } else { for (int i = 1; i <= n; i++) { cout << a[i] << " \n"[i == n]; } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { Solve(); } return 0; }