廣度優先搜尋,分支限界- ZOJ - 1136 Multiple

許佳佳233發表於2016-06-23

Description

a program that, given a natural number N between 0 and 4999 (inclusively), and M distinct decimal digits X1,X2..XM (at least one), finds the smallest strictly positive multiple of N that has no other digits besides X1,X2..XM (if such a multiple exists).

The input file has several data sets separated by an empty line, each data set having the following format:

On the first line - the number N
On the second line - the number M
On the following M lines - the digits X1,X2..XM.

For each data set, the program should write to standard output on a single line the multiple, if such a multiple exists, and 0 otherwise.

An example of input and output:

Input

22
3
7
0
1

2
1
1

Output

110
0

思路

1、題目一看就是搜尋:把數的組合從小到大一個個嘗試,如果可以是N的倍數,那麼就輸出。

2、要把組合的數從小到大輸出就需要先把數升序排序,然後從小到大依次嘗試,很容易就想到要用廣度優先搜尋(佇列)

3、如果僅僅是暴力,那麼此題也就僅僅是一個bfs的demo了,但事實不是。此題還需要考慮整數越界的問題,如果N=4999,組合成的數是N的上幾萬倍這時候可能就會越界。(據說使用 long long也可以,但是筆者還是考慮了下)所以要使用string來儲存組合成的整數。那麼如何能夠把問題縮小化呢?很簡單就是儲存餘數。
打個比方:
N=3,t1=10,t2=4。
(t1*10+t2)%N=2 //這是我們一般的處理方式
t1%N=1,(1*10+t2)%N=2 //這是能夠縮小數量級的處理方式

4、num[5010]的設定絕對是程式碼中最巧妙的一點,主要作用是對餘數的判斷。num[i]=1時表示餘數為i的情況已經出現過,num[i]=0時表示餘數為i的情況還沒有出現過。在程式碼中主要有兩個作用:
一、減少重複遍歷。如果出現同樣的餘數,代表在此位置之前已經出現過了這樣的餘數,在此位置之前的定然是更小的數。此題最終結果是找出最小的倍數,所以就沒有必要幫相同的情況放進佇列了。(如果之前更小的數不能得出結果,餘數相同,更大的數也必然得不出結果)
二、避免死迴圈。如果N=2,M=2,X1=3,X2=5。很容易可以明白,這種情況是怎麼都得不到結果的。如果有沒有對重複的餘數的判斷,那麼會一直有數入站,佇列永遠不會空,while也永遠不會結束。但是如果有了餘數這個限制條件,當所有餘數都已經出現過,就不會再有數入佇列,因此當佇列遍歷完的時候,就可以return “0”,表示找不到這個數。

程式碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;

int n,m;//n為要除以的數,m表示有m個數字組合
int p[12];//p用來儲存m個數
int num[5010];//num[i]=1時表示餘數為i的情況已經出現過,num[i]=0時表示餘數為i的情況還沒有出現過

struct node
{
    string s;//表示當前數字的string格式
    int res;//當然數字除以n的餘數
};

string bfs()
{
    queue<node> q;
    for(int i=0; i<m; i++)//先把把每一個數都放進佇列,都只有個位數
    {
        node ss;
        ss.s+=char('0'+p[i]);//把int轉化為數字
        ss.res=p[i];

        if(ss.s=="0")//數字為0的情況沒意義,直接continue
            continue;
        q.push(ss);
    }

    while(!q.empty())
    {
        node s=q.front();
        q.pop();

        for(int i=0; i<m; i++)
        {
            node t;
            t.s=s.s+char('0'+p[i]);
            t.res=(10*s.res+p[i])%n;

            if(num[t.res]==1)//如果此餘數已經出現過,就不放入佇列了
                continue;
            else
            {
                num[t.res]=1;//如果上面的情況都不是,表示此數沒有出現過相同的餘數,則進行處理
                q.push(t);
            }

            if(t.res==0)//如果餘數等於0,表示已經找到
                return t.s;

        }
    }
    return "0";
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0)//除數等於0沒有意義,直接輸出0
        {
            printf("0\n");
            continue;
        }

        memset(num,0,sizeof(num));
        for(int i=0; i<m; i++)
            scanf("%d",p+i);

        sort(p,p+m);
        cout<<bfs()<<endl;
    }
    return 0;
}

相關文章