POJ 3373 Changing Digits 记忆化搜索
2013-08-10 15:13
381 查看
这道题我是看了别人的题解才做出来的。题意和题解分析见原文/article/1968975.html
这里写一下自己对题目的理解。
1. 根据k的最大范围直接搜索n最后5位的方法是错误的,因为它并不能保证所求结果为最小。因为题目要求最后结果m要尽量小,而改变n的高位能够得到更小的值。k<n的限制条件表明解必然存在,而我们搜索的最大可修改位数应该和n的位数len相等。
2. 最终结果m必须满足:最高位非0且与n位数相同 (m若等于0不视为最高位为0),m能被k整除。在此基础上,还有两个条件,它们的优先级为:m与n对应位置上的数不相等的个数尽量少 > m尽量小。即最终得到的结果不一定是最小的,但一定是与n对应位置数字不相等个数最少的。这也决定了本题dfs的写法。
3. 面对大量大数取模运算,利用mod[i][j]预处理(10^i)*j模k的值,节省了大量时间。这样在dfs的过程中,改变某一位后的m%k的值也能计算出来了。搜索时为了求得的结果最小,先从m<n开始搜,然后再搜m>n。
前者有res = (m_modk - (mod[i][n[i]] - mod[i][j]) + k) % k;
后者有res = (m_modk + (mod[i][j] - mod[i][n[i]]) + k) % k;
4. flag数组的引入是为了剪枝。如果当搜索区间为[0, pos]且此时m模k为m_modk时,如果最多修改restnum位不能成功,则修改次数少于restnum时更不可能成功,因此就不用搜索下去了。flag[pos][m_modk]始终维护上述情况无法成功的最大restnum。
我的代码
这里写一下自己对题目的理解。
1. 根据k的最大范围直接搜索n最后5位的方法是错误的,因为它并不能保证所求结果为最小。因为题目要求最后结果m要尽量小,而改变n的高位能够得到更小的值。k<n的限制条件表明解必然存在,而我们搜索的最大可修改位数应该和n的位数len相等。
2. 最终结果m必须满足:最高位非0且与n位数相同 (m若等于0不视为最高位为0),m能被k整除。在此基础上,还有两个条件,它们的优先级为:m与n对应位置上的数不相等的个数尽量少 > m尽量小。即最终得到的结果不一定是最小的,但一定是与n对应位置数字不相等个数最少的。这也决定了本题dfs的写法。
3. 面对大量大数取模运算,利用mod[i][j]预处理(10^i)*j模k的值,节省了大量时间。这样在dfs的过程中,改变某一位后的m%k的值也能计算出来了。搜索时为了求得的结果最小,先从m<n开始搜,然后再搜m>n。
前者有res = (m_modk - (mod[i][n[i]] - mod[i][j]) + k) % k;
后者有res = (m_modk + (mod[i][j] - mod[i][n[i]]) + k) % k;
4. flag数组的引入是为了剪枝。如果当搜索区间为[0, pos]且此时m模k为m_modk时,如果最多修改restnum位不能成功,则修改次数少于restnum时更不可能成功,因此就不用搜索下去了。flag[pos][m_modk]始终维护上述情况无法成功的最大restnum。
我的代码
#include<stdio.h> #include<string.h> #define maxn 103 #define mk 10003 int len, k, n[maxn], mod[maxn][10]; int m[maxn], flag[maxn][mk]; char num[maxn]; void init_mod()//mod[i][j]表示(10^i)*j模k的值 { for (int i = 0; i <= 9; i++) mod[0][i] = i % k; for (int i = 1; i < len; i++) for (int j = 0; j <= 9; j++) mod[i][j] = (mod[i-1][j] * 10) % k; } int dfs(int pos,int restnum,int m_modk) { if (!m_modk) return 1; if (!restnum || pos < 0) return 0; if (restnum <= flag[pos][m_modk]) return 0;//剪枝 for (int i = pos; i > -1; i--)//搜索比n小的数,要尽可能小,则从高位开始 for (int j = 0; j < n[i]; j++) { if (i == len - 1 && !j) continue; int res = (m_modk - (mod[i][n[i]] - mod[i][j]) + k) % k; m[i] = j; if (dfs(i - 1, restnum - 1, res)) return 1; m[i] = n[i]; } for (int i = 0; i <= pos; i++)//搜索比n大的数,要尽可能小,则从低位开始 for (int j = n[i] + 1; j < 10; j++) { int res = (m_modk + (mod[i][j] - mod[i][n[i]]) + k) % k; m[i] = j; if (dfs(i - 1, restnum - 1, res)) return 1; m[i] = n[i]; } flag[pos][m_modk] = restnum;//能运行到这里说明搜索失败,更新剪枝数值 return 0; } int main() { while (~scanf("%s%d", num, &k)) { int n_modk = 0; len = strlen(num); init_mod(); for (int i = 0; i < len; i++)//将num反序存入整型数组 { n[i] = num[len-1-i] - '0'; m[i] = n[i]; n_modk = (n_modk + mod[i][ n[i] ]) % k;//计算n % k } memset(flag, 0, sizeof(flag)); int ok = 0; for (int i = 1; i <= len; i++)//从小到大枚举可以修改的位数 if (dfs(len - 1, i, n_modk)) break; for (int i = len - 1; i > -1; i--) printf("%d", m[i]); printf("\n"); } return 0; }
相关文章推荐
- poj 3373 Changing Digits(记忆化搜索)
- POJ_3373_Changing Digits_DP
- POJ 3373 Changing Digits
- POJ 3373 记忆化搜索 难在转移上的 dp...
- poj 3373 Changing Digits
- poj 3373 Changing Digits
- poj3373——Changing Digits
- 【poj 3373】Changing Digits 题意&题解&代码(C++)
- POJ 3373 Changing Digits
- poj3373--Changing Digits(DFS+剪枝///记忆化)
- POJ 3373 Changing Digits(DP)
- poj 3373 Changing Digits(打表 + dfs + 剪枝 )
- POJ-3373-Changing Digits
- POJ 3373 Changing Digits
- poj 3373 Changing Digits
- pku 3373 Changing Digits(记忆化搜索)
- POJ 3373 Changing Digits 好蛋疼的DP
- poj 3373 Changing Digits (DFS + 记忆化剪枝+鸽巢原理思想)
- POJ 3373--Changing Digits
- POJ 3373 Changing Digits 好蛋疼的DP