您的位置:首页 > 其它

POJ 1465 Multiple BFS + 余数判重

2012-08-15 23:34 316 查看
题意是: 给出一个 0 - 4999 的数 N ,在给出 M 个0-9的数,判断这M个数字能不能构成一个数是N的倍数,如果有输出最小的,如果没有输出0。

初次做的 时候犯了很多错误。。。CE TlE 等等各种, 初次做这个题目的时候用了 long long 结果就悲剧了。。。 想了想 long long 明显hold 不住 因为结果可能很大。

不信的话 跑一下这组数据 4999 1 3 位数早就超了20 位。

第二个 令我 wa 爆的是 当 N =0 的时候 一直RE ,还查不出哪里出了错了。。N=0 时特判一下就可。

此题用BFS 。。 这个题好在 用 余数判重剪枝。。

BFS 如果不加以剪枝,一定会搜索的情况会很庞大。所以应该用余数判重 。

为什么可以用余数判重?

A=a*N +e 即A%N =e

B= b*N+e即B%N=e

当A B mod N的余数相同时,如果先出现A 。

在A 后加上一个数 i 时 , 新的数 C = 10 *a*N + 10 *e+i;

同样 B后加上数 i 时 , D = 10*b*N +10*e+i; 由于C D 前边 10*a*N 和 10*b*N 都是N的倍数 ,则C D mod N 的余数都是有 10*e+i 决定的。

于是 C D mod N 同余。

因此 A B 同余 都添加上 i 之后 新的两个数C D也是同余的。在无论添加多少个数,新生成的两个数也是同余的。因此 在A 之后如果不出现 N的倍数 ,则

在B之后也不会出现。 在A 之后出现,那B之后也会出现。 有因为要求求最小值。所以只需要搜索min(A,B)之后的 ,对于另外一个数之后就不用搜索了。

因为对M个排序后,先填入的是小的值,所以 A B 先出现的值小。所以后出现的同余的数 就不用搜索了。。

因此可以余数判重后剪枝。。。。

附上我的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int num[12];
int n;
int m;
int exist[5200];//  判重    0 表示没出现 1表示余数出现过
char answer[5200]; // 最后答案存储char 数组里之所以不用string 是因为 每次的 string 的拼接是 O(L)的复杂度  。 如果用java的话 用StringBuilder可以
struct node {       // 节点类  把每次的添加的 数 看成在树上添加节点
int fid ; //存储父节点的索引 , 也可用用父节点的指针引用
int num; // 表示 添加入的 数子
int mod; // 表示  当前的数加入后的余数
}tree[5200];   // 因为经过了判重,最多可加入的节点是 N个 也就是 余数的个数。。
struct queue{    // 自己手写的queue
int head;
int tail;
int num[5200];
void push (int a){
num[tail]=a;
tail++;
}
int front(){
return num[head];
}
void pop(){
head++;
}
bool empty(){
if(head==tail)
return true;
return false;
}

void clear(){
tail = head = 0;
}
} qq={0,0};
void init_exist(){
for(int i=0;i<=n;i++){
exist[i]=0;
}
}
void bfs (){
if(!n){   // n=0 特判
cout << 0<<endl;
return ;
}
int idx = 1;
qq.clear(); // queue 自己写的方法 表示清空  多组数据用一个队列就够了
init_exist();
for(int i=1;i<=m;i++){   // 现将 这M个数填入队列
int nm = num[i];
int mod = nm%n;
if(!num[i]){  // 如果  0 不入队
continue;
}
if(!mod){  // 余数出现 不入队
cout << nm<<endl;
return ;
}
if(!exist[mod]){
exist[mod] = 1;
tree[idx].fid = 0;  // 表示是最初的节点。。 无父亲节点。。
tree[idx].num = nm;
tree[idx].mod = mod;
qq.push(idx++);
}

}
while(!qq.empty()){
int  id = qq.front();
qq.pop();
int md=tree[id].mod;
for(int i=1;i<=m;i++){
int nm = num[i];
int add_num = md*10+nm;
int add_mod = add_num%n;
if(!add_mod){ // 判读 余数如果为 0 就输出 。。。。
int ch_idx =1;
answer[ch_idx++]=nm+'0';
answer[ch_idx++]=tree[id].num+'0';
while(tree[id].fid){
id  = tree[id].fid;
answer[ch_idx++]=tree[id].num+'0';
}  //一直找到开始的节点  一开始的节点 的fid =0
for(int k = ch_idx-1;k>=1;k--){  //  逆向填入的值 逆向输出。。。。
printf("%c",answer[k]);
}
cout <<endl;
return ;
}
if(!exist[add_mod]){
exist[add_mod] = 1;
tree[idx].fid = id;   // 添加新的节点
tree[idx].num = nm;
tree[idx].mod =add_mod;
qq.push(idx++);
}
}
}

cout << 0<<endl;
return ;
}

int main(){
while(scanf("%d",&n)!=EOF){
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&num[i]);
}
sort(num+1,num+m+1);
bfs();
}
return 0;

}


这个程序在poj跑了 32 MS 。。感觉还行 。。 貌似就这个题而言 poj上的数据比zoj强些 ,我的在poj TLE的程序在 zoj上 AC
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: