POJ 1465 Multiple BFS + 余数判重

  题意是: 给出一个 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

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值