poj 3373(记忆化搜索)

本文介绍了一种使用记忆化搜索技术解决特定数学问题的方法,即在保持数字位数不变的情况下,找到能够被给定整数整除的最小数字,并且在相同位置上与原始数字差异最小。通过构建模运算数组和深度优先搜索,有效剪枝搜索空间,最终得到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Changing Digits

点击打开题目链接

Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 2976 Accepted: 962
Description


Given two positive integers n and k, you are asked to generate a new integer, say m, by changing some (maybe none) digits of n, such that the following properties holds:


m contains no leading zeros and has the same length as n (We consider zero itself a one-digit integer without leading zeros.)
m is divisible by k
among all numbers satisfying properties 1 and 2, m would be the one with least number of digits different from n
among all numbers satisfying properties 1, 2 and 3, m would be the smallest one
Input


There are multiple test cases for the input. Each test case consists of two lines, which contains n(1≤n≤10100) and k(1≤k≤104, k≤n) for each line. Both n and k will not contain leading zeros.


Output


Output one line for each test case containing the desired number m.


Sample Input


2
2
619103
3219
Sample Output


2
119103

Sourc

本题参考别人的博客,确实有些技巧想不到。

参考博客:http://www.cnblogs.com/fenshen371/p/3250109.html

大致题意:
给出2个整数n(n<10^100)和k(k<10000),求满足以下条件的整数m
 1、m与n位数相同
 2、m能被k整除
 3、满足以上两点时,m和n在相同位置的地方,数字不同的个数最少
 4、满足以上三点时,m值最小

 flag数组的引入是为了剪枝。如果当搜索区间为[0, pos]且此时m模k为m_modk时,如果最多修改restnum位不能成功,则修改次数少于restnum时更不可能成功,因此就不用搜索下去了。flag[pos][m_modk]始终维护上述情况无法成功的最大restnum。

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#define N 110
#define M 10010
using namespace std;
int k,len;
int mod[N][10];
int num[N],flag[N][M];
int m[N];
char n[N];
int n_modk;
void init()///mod[i][j]表示10^i*j%k的值(这个地方的处理好奇妙,膜拜啊)
{
    int i,j;
    n_modk=0;
    len=strlen(n);
    for(i=0; i<10; i++)
        mod[0][i]=i%k;
    for(i=1; i<len; i++)
    {
        for(j=0; j<10; j++)
            mod[i][j]=(mod[i-1][j]*10)%k;
    }

    for(i=0; i<len; i++)///初始化求n%k的值
    {
        num[i]=n[len-i-1]-'0';
        m[i]=num[i];
        n_modk=(n_modk+mod[i][num[i]])%k;
    }
}
bool dfs(int pos,int resnum,int m_modk)///深搜(三个参数的设计,pos是搜索到的n中得位置,resnum是搜索的剩余步数,m_modk是m对于k的余数)
{
    if(!m_modk)///当余数为零时即可找到输出返回(保证找到的第一个解就是最优解,why??)
    {
        int i;
        for(i=len-1; i>=0; i--)
            printf("%d",m[i]);
        printf("\n");
        return 1;
    }
    if(!resnum||pos<0)///当剩余次数wei0或者pos小于零时,返回
        return 0;
    if(resnum<=flag[pos][m_modk]) return 0;///这里又是一个强减枝的地方()
    int i,j;
    for(i=pos; i>=0; i--)///对于小于n的从高位开始搜索(why???,在保证条件三的前提下,保证最小即条件4)
    {
        for(j=0; j<num[i]; j++)
        {
            if(i==len-1&&j==0)///去除前导零的情况
                continue;
            int res=(m_modk-(mod[i][num[i]]-mod[i][j])+k)%k;///重新计算m_modk的值(不得不说,这让我想不到,其实就是很简单的一部转化,但是就是想不到,智商压制)
            m[i]=j;
            if(dfs(i-1,resnum-1,res))
                return 1;
            m[i]=num[i];
        }
    }
    for(i=0; i<=pos; i++)///对于大于n的m,从低位开始搜索
    {
        for(j=num[i]+1; j<10; j++)
        {
            int res=(m_modk+(mod[i][j]-mod[i][num[i]])+k)%k;///
            m[i]=j;
            if(dfs(i-1,resnum-1,res))
                return 1;
            m[i]=num[i];
        }
    }
    flag[pos][m_modk]=resnum;///记录不可能的数字的改变数
    return 0;
}
int main()
{
    int i;
    while(~scanf("%s%d",n,&k))
    {
        init();
        memset(flag,0,sizeof(flag));
        for(i=1; i<=len; i++)
        {
            if(dfs(len-1,i,n_modk))
                break;
        }
    }
    return 0;
}


记忆化搜索:

记忆化搜索的实质是动态规划,效率也和动态规划接近,形式是搜索,简单直观,代码也容易编写,不需要进行什么拓扑排序了。
可以归纳为:记忆化搜索=搜索的形式+动态规划的思想。
记忆化搜索我认为最主要的是:如何记录重复的状态或者说记录子问题的结果防止重复计算,这样就会加快搜索效率,特别是对于重复性比较高搜索方式。例如poj 1579,只要在正常的递归前面加上几个记录子问题的结果避免重复计算就可以了;
具体请看博客:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值