HDU 1226 超级密码(数学 bfs)

传送门:

http://acm.hdu.edu.cn/showproblem.php?pid=1226

超级密码

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4901    Accepted Submission(s): 1591


Problem Description
Ignatius花了一个星期的时间终于找到了传说中的宝藏,宝藏被放在一个房间里,房间的门用密码锁起来了,在门旁边的墙上有一些关于密码的提示信息:
密码是一个C进制的数,并且只能由给定的M个数字构成,同时密码是一个给定十进制整数N(0<=N<=5000)的正整数倍(如果存在多个满足条件的数,那么最小的那个就是密码),如果这样的密码存在,那么当你输入它以后门将打开,如果不存在这样的密码......那就把门炸了吧.

注意:由于宝藏的历史久远,当时的系统最多只能保存500位密码.因此如果得到的密码长度大于500也不能用来开启房门,这种情况也被认为密码不存在.
 
Input
输入数据的第一行是一个整数T(1<=T<=300),表示测试数据的数量.每组测试数据的第一行是两个整数N(0<=N<=5000)和C(2<=C<=16),其中N表示的是题目描述中的给定十进制整数,C是密码的进制数.测试数据的第二行是一个整数M(1<=M<=16),它表示构成密码的数字的数量,然后是M个数字用来表示构成密码的数字.两个测试数据之间会有一个空行隔开.

注意:在给出的M个数字中,如果存在超过10的数,我们约定用A来表示10,B来表示11,C来表示12,D来表示13,E来表示14,F来表示15.我保证输入数据都是合法的.
 
Output
对于每组测试数据,如果存在要求的密码,则输出该密码,如果密码不存在,则输出"give me the bomb please".

注意:构成密码的数字不一定全部都要用上;密码有可能非常长,不要试图用一个整型变量来保存密码;我保证密码最高位不为0(除非密码本身就是0).
 
Sample Input
3 22 10 3 7 0 1 2 10 1 1 25 16 3 A B C
 
Sample Output
110 give me the bomb please CCB
Hint
Hint
Huge input, scanf is recommended.
 
Author
Ignatius.L
 
Source
 
Recommend
Ignatius.L   |   We have carefully selected several similar problems for you:  1043 1044 1072 1067 1195 
 
题目意思:
给出n,c,m,密码必须是n这个十进制数的整数倍,c代表这个密码是C进制数,m代表这个密码只有m种字符构成,而且密码不能长于500
 
自己没有想出来,看了网上大牛的思想才懂:

不要试图去枚举十进制n的倍数来然后看看给的几个数字能否凑出来,可以想象2要翻倍多少次才能达到500位的长度,

因此要从给定的数字入手,枚举数字的各种组合情况,判断是不是n的整数倍。

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<queue>
#include<set>
#include<map>
#include<string>
#include<memory.h>
using namespace std;
int n,c,m,book[20],vis[5005];
int flag=0;
struct node
{
    int s[505],len;
};
queue<node> q;
int mod(node t)
{
    int temp=0;
    for(int i=0;i<t.len;i++)
    {
        temp=(temp*c+t.s[i])%n;
    }
    return temp;
}
void pr(node a)
{
    for(int i=0;i<a.len;i++)
    {
        if(a.s[i]<=9)
            printf("%d",a.s[i]);
        else
            printf("%c",a.s[i]+'A'-10);
    }
    printf("
");
    return ;
}
void bfs()
{
    node t;
    t.len=0;
    int v;
    for(int i=1;i<16;i++)//先出来第一个结点,因为密码第一位不能为0
    {
        if(book[i]==1)
        {
            t.s[0]=i;
            t.len=1;
            v=mod(t);
            if(v==0)
            {
                pr(t);
                flag=1;
                return ;
            }else
            {
                if(vis[v]==0)
                {
                    vis[v]=1;
                    q.push(t);
                }
            }
        }
    }
    while(!q.empty())
    {
        t=q.front();
        q.pop();
        for(int i=0;i<16;i++)
        {
            if(book[i]==1)
            {
                t.s[t.len]=i;
                t.len++;
                v=mod(t);
                if(v==0)
                {
                    pr(t);
                    flag=1;
                    return ;
                }else
                {
                    if(vis[v]==0&&t.len<499)//需要判断一下数位
                    {
                        vis[v]=1;
                        q.push(t);
                    }
                }
                t.len--;
            }
        }
    }
    return ;
}
int main()
{
    int T;
    char s[2];
    cin>>T;
    while(T--)
    {
        flag=0;
        memset(vis,0,sizeof(vis));
        memset(book,0,sizeof(book));
        while(!q.empty())
        {
            q.pop();
        }
        cin>>n>>c>>m;
        getchar();
        for(int i=0;i<m;i++)
        {
            cin>>s;
            if(s[0]<='9'&&s[0]>='0')
            {
                book[s[0]-'0']=1;
            }else
            {
                book[10+s[0]-'A']=1;
            }
        }
        if(n!=0)
        {
            bfs();
            if(flag==0)
                cout<<"give me the bomb please"<<endl;
        }else//n==0的话,不能取模,因此不能入队,特判一下
        {
            if(book[0])
                cout<<"0"<<endl;
            else
                cout<<"give me the bomb please"<<endl;
        }
    }
}

而且这样的话还有一个强剪枝就是如果余数相同就没必要再次入队了,

因为无论是大数还是小数如果模n同余那么再添上其他数字的话再模n还是同余的,

这样每个余数最多在队列里出现一次,也就是说队列中最多出现5000个结点,大大优化了时间。

还有要注意的一点是n=0时的特判。

code:

原文地址:https://www.cnblogs.com/yinbiao/p/9356197.html