POJ1167 公交线路 【例题详解】

    题意:给出一个小时(0-59分钟)内某站地经过的公交车记录,已知同一条公交线路在这一站停车的时间间隔相同,同一时刻可能经过多辆车,问这一个时刻表中,至少包括了多少个公交线路;
    特别提醒,每条线路至少经过了两次,而且公交线路最多有十七条;
   
    看到这道题,依然是用深搜,那么就需要预处理所有的可能的公交线路情况,然后带入dfs看是否能得到一组合法解(线路数),最后搜到的所有的合法解中最小的那个ans就是最终结果;
    对于这道题,可以用一个结构体存储所有可能的公交线路基本信息;
    下面是相关剪枝或优化:
1.可以吧所有可能的公交线路信息(结构体)按经过次数多少排序,从大到小,那么搜到一个点时,如果目前已经确定的公交线路数加上 剩余记录数/当前这个点的经过次数 要大于目前已经确定的最小合法解,那么这一条一定非最优,直接剪枝;这里会比较绕,不是因为剩下的记录数跟这一点的经过次数之比值一定是这条路其他的的公交线路数,而是因为当前的经过次数一定是接下来可能搜索到的线路的经过次数中最大的,二者作除法之后,得到的一定是可能的最小的路线匹配,但这种可能的几率很小很小,他只是一个无论如何也不会错的这样一个极限;
2.如果之前搜索的时候把这条线路的某些记录占了,那经过判断,如果这条公交线路目前已经不成一条线了,那就不理他了继续循环;
 
大体的重要想法就这两个,难点主要是第一个;其次需要注意,这里17条线路是一个极限值,是要通过一次次比较来降低的,想到迭代加深可能会误入歧途;
下面看看代码,我给了比较详细的注释;
 
 1 //王侯将相宁有种乎! 
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstdlib>
 6 #include<cstring>
 7 #include<string>
 8 #include<algorithm>
 9 #include<queue>
10 using namespace std;
11 const int MAXN=1505;
12 int ans,cnt[60],n,m,k;
13 struct node{
14     int start,interval,times;
15 } r[MAXN];
16 bool cmp(node a,node b){
17     return a.times>b.times;
18 }
19 bool pd(int time,int inter)
20 {
21     while(time<60){
22         if(!cnt[time]) return false;
23         time += inter;
24     }
25     return true;
26 }//判断能否成为一条公交线路; 
27 void dfs(int k,int num,int sumr){
28 //k表示目前搜到的线路, num表示当前已经有多少个记录被匹配,sumr是共用了多少个公交线路; 
29     if(num==n){
30         if(ans>sumr) ans=sumr;
31         return ;
32     }
33     for(int i=k;i<=m;i++){
34         if(sumr + (n-num) / r[i].times >= ans) return ;//题解中说的剪枝1 
35         if(!pd(r[i].start,r[i].interval))continue ;//题解中说的2 
36         for( int j = r[i].start; j<60; j += r[i].interval ){
37             cnt[j]--;//占用一条线路; 
38         }
39         dfs(i, num + r[i].times, sumr+1);
40         for( int j = r[i].start; j<60; j += r[i].interval ){
41             cnt[j]++;//回溯; 
42         }
43     }
44     return ;
45 }
46 int main()
47 {
48     int t;
49     while(~scanf("%d",&n)){
50         memset(cnt,0,sizeof(cnt));
51         for(int i=1;i<=n;i++){
52             scanf("%d",&t);
53             ++cnt[t];//出现的次数; 
54         }
55         m=0;
56         for(int i=0;i<30;i++){
57             for(int j=i+1;j<60-i;j++){
58                 if(pd(i,j)){
59                     r[++m].start=i;
60                     r[m].interval=j;
61                     for(int l=r[m].start;l<60;l+=r[m].interval) r[m].times++;
62                     //枚举可能的从i到j的线路; 
63                 }
64             }
65         }
66         sort(r+1,r+m+1,cmp);//大到小排序; 
67         ans=17;
68         dfs(1,0,0);
69         printf("%d\n",ans);
70     }
71     return 0;
72 }
View Code

 

posted @ 2018-04-05 10:16  杜宇一声  阅读(578)  评论(0编辑  收藏  举报