1251 字
6 分钟
Shortest Statement Ever
[原题链接](Problem - D - Codeforces)
问题理解与转化
1.题目概述
- 题目:给你 x 和 y,让你找出两个非负整数 p 和 q,满足 且 最小
输入:
70 01 13 67 114 4123 3211073741823 1073741822输出:
0 02 13 86 94 3128 3211073741824 1073741822关键约束:数据量在 左右,无法通过直接暴力计算算出可能的答案
2. 核心思路阐述
解题思路
- 很明显,本题是用的是数位运算的办法,通过拆分每一位的值,通过 -1 或者 -0的操作,模拟相减的情况,使得 且
- 由于情况复杂多样,我们无法通过贪心的方法解决该问题,只有用动态dp的方法去解决该问题
- 只有三种情况
- 大于 等于 小于
- 可以考虑直接暴力查找这 3 种情况,再去暴力遍历 p , q 在第 i 位是1还是 0 (满足不同时为 1 即可)
- 状态表示:
- 我们设立 表示 计算到第 i 位,q 的状态是 和 p的状态是 所得到的最小值
- 状态转移:
关键观察
- 若计算到第 位时,x 和 p 相等, 中 则 p一定大于x 无论后续 p 有多少个小于 x
- 比如:
3. 详细算法设计
算法步骤
- 先将 f 数组初始化为 INF ,只有 表示所有情况都是从 该状态转移来的。
- 暴力拆解 p 和 x , q 和 y的关系 0 1 2
- = 1 : 需要判断当前点情况 与 x 的情况 ,若
- = 0 : 保持等于 0
- = 2 : 保持等于 2
- 证明存在该情况 可以计算
- 由于题目要求计算出 原来的 p 和 q, 因此我们得通过得到的 最小 进行回推
- 我们还得存储每一次状态转移的 和
- 由于我们得到的是 第 0 位,得从 0 推向 31 位 故要存储
- 同理再存储 的值,最后逆向回溯当前值即可
4. 正确性证明
正确性证明
- 我们用动态规划的方法计算了所有可能的情况,并按照最小来转移
- 因此一定存在我们想要的答案
5. 复杂度分析
复杂度分析
- 时间复杂度 :
- 空间复杂度 :
6. 代码实现
#include <bits/stdc++.h>using namespace std;#define int long longusing PII = pair<int,int>;
const int N = 2e5 + 10,INF = 1e18;
int f[33][3][3]; // 表示 第 i 位 p 与 x 的大小比较 q 与 x 的大小比较PII pre_st[33][3][3], coi[33][3][3];// 0 p > x 1 p = x 2 p < xvoid solve(){ int x, y; cin >> x >> y; //初始化 for (int i = 0; i <= 31; ++i) for (int j = 0; j < 3; ++j) for (int k = 0; k < 3; ++k) f[i][j][k] = INF;
f[31][1][1] = 0; //数位dp过程 for(int i = 30; i >= 0; i--){ int xi = (x >> i) & 1; int yi = (y >> i) & 1; for(int stp = 0; stp < 3; stp++){ for(int stq = 0; stq < 3; stq++){ if(f[i + 1][stp][stq] == INF) continue; for(int p = 0; p < 2 ;p++){ for(int q = 0; q < 2; q++){ if(p & q == 1) continue; int nstp = stp; if(nstp == 1){ if(p > xi) nstp = 0; else if(p < xi) nstp = 2; } int nstq = stq; if(nstq == 1){ if(q > yi) nstq = 0; else if(q < yi) nstq = 2; } int t = 0; //计算 当 p 与 x 的大小关系 为 nstp 时的代价 if(nstp == 0){ t += (p - xi) << i; }else if(nstp == 2){ t += (xi - p) << i; } //计算 当 q 与 x 的大小关系 为 nstq 时的代价 if(nstq == 0){ t += (q - yi) << i; }else if(nstq == 2){ t += (yi - q) << i; } //更新 当 p 与 x 的大小关系 为 nstp 且 q 与 x 的大小关系 为 nstq 时的最小代价 if(f[i + 1][stp][stq] + t < f[i][nstp][nstq]){ f[i][nstp][nstq] = f[i + 1][stp][stq] + t; pre_st[i][nstp][nstq] = {stp, stq}; coi[i][nstp][nstq] = {p, q}; } } } } }
}
int mi = INF; int stp = -1, stq = -1; for(int j = 0; j < 3; j++){ for(int k = 0; k < 3; k++){ if(f[0][j][k] < mi){ mi = f[0][j][k]; stp = j; stq = k; } } } //回推 p 和 q 的值 int p = 0, q = 0; for(int i = 0; i <= 30; i++){ int nstp = pre_st[i][stp][stq].first; int nstq = pre_st[i][stp][stq].second; p |= coi[i][stp][stq].first << i; q |= coi[i][stp][stq].second << i; stp = nstp; stq = nstq; } cout << p << " " << q << endl;}
signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int _ = 1; cin >> _; while(_--){ solve(); }}7. 拓展与变种
相关题目
问题变种
实际应用
8. 总结与收获
总结
- 本题最难的我认为还是,状态转移的表示,初见该类题目难以想到如何状态转移
核心知识点
- [数位DP]
思维模式
- 分析每一位的转移,找到最优的方案
学习建议
- 推荐多写写
Shortest Statement Ever
https://blog.rookiesama.space/posts/题解/shortest-statement-ever/ 部分信息可能已经过时









