首页 > 代码库 > poj 1167 DFS

poj 1167 DFS

  1 /*
  2 题意:给出0~59的一排数字表示某一时刻出现了1辆bus,其中数字重复出现表示多辆bus同时出现,这些bus是
  3 由多个bus线路组成的,每条线路都有一个时间间隔,而且同一线路的bus在0~59肯定会出现两次或以上,如果
  4 有两条线路的间隔相同且到达时刻相同也算作两条不同的线路,问最少有多少条线路。
  5 
  6 题解:DFS
  7 这题是一题相对较难的搜索,关键在于找出状态以及剪枝;
  8 对于每条bus线路都由开始时刻start和间隔interval组成,这样就找出了表示一条线路的状态,而且这些线路
  9 还有数量的属性(题意所说的时间间隔相同的线路当作不同,我在这里用数量来表示这种不同,因为要求的只是
 10 最少的方案),通过DFS找出方案使得线路数目最小;每条线路都可以有x种状态(x的范围是0~min(time[i]),
 11 然后对time[]数组中的值进行修改后继续DFS搜素下一个线路;
 12 
 13 剪枝:
 14 其中有两个很重要的剪枝:
 15 1.pans > ans的剪枝,即当前的答案大于已经有的答案时剪枝(ans初始为17,因为答案必须小于等于17)
 16 2.线路的剪枝:
 17 结构体line的times属性表示的是该线路的班次(即0~59内间隔出现次数),当记录的出现的次数相等时,班次越
 18 大,平均下去的线路数目越少,这个剪枝发挥了很大的作用:
 19 pans表示当前已有线路数目,sum表示总共记录的所有bus的出现次数,tsum表示当前已遍历的记录的总数
 20 pans + (sum-tsum)/lines[pline].times
 21 上式求的是当前可能的结果的最小值:剩余记录次数sum-tsum使得线路数目最小的情况是,当前的这个线路能
 22 够把所有的记录均摊下去,得出的结果加上pans就是可能的最小值(通过先前的排序保证了times是递减的,这样
 23 后面的线路种类不可能使线路数目更小),然后比较判断是否继续搜索;
 24 这个剪枝非常的巧妙,因为本来对于start和interval状态的搜索顺序并不会影响整个结果,因为只是一个选与
 25 不选的问题,而且始终是要将所有状态都全部搜索一遍
 26 */
 27 #include <cstdio>
 28 #include <cstring>
 29 #include <algorithm>
 30 
 31 using namespace std;
 32 
 33 int ans;
 34 int time[80];
 35 
 36 struct line
 37 {
 38     int start,interval;
 39     int times;
 40     bool operator<(const line &t)const
 41     {
 42         return times > t.times;
 43     }
 44 }lines[1000];
 45 int top;
 46 int sum,tsum;
 47 
 48 int check(int start, int interval) // 检查该线路是否还能走并且返回记录次数最小的值
 49 {
 50     int ret = 500;
 51     for(int i=start; i<60; i+=interval)
 52     {
 53         if (!time[i])
 54             return -1;
 55         else
 56             ret = min(ret,time[i]);
 57     }
 58     return ret;
 59 }
 60 
 61 void dfs(int pline, int pans)
 62 {
 63     if (pans > ans)
 64         return ;
 65     if (tsum == sum) // 所有的记录被遍历时比较结果
 66     {
 67         ans = min(pans,ans);
 68         return ;
 69     }
 70     if (pline >= top)
 71     {
 72         return ;
 73     }
 74 
 75     int c = check(lines[pline].start,lines[pline].interval);
 76     if (c != -1)
 77     {
 78         if (pans + (sum-tsum)/lines[pline].times >= ans)
 79             return ;
 80         for(int j=1; j<=c; j++)
 81         {
 82             for(int k=lines[pline].start; k < 60; k+=lines[pline].interval)
 83             {
 84                 tsum += j;
 85                 time[k] -= j;
 86             }
 87             dfs(pline+1,pans+j); // 当前线路要j条,然后继续下一搜索
 88             for(int k=lines[pline].start; k < 60; k+=lines[pline].interval)
 89             {
 90                 tsum -= j;
 91                 time[k] += j;
 92             }
 93         }
 94     }
 95     dfs(pline+1,pans); // 当前线路不要,继续搜索下一线路
 96 }
 97 
 98 int main(void)
 99 {
100     int n;
101     while (~scanf("%d",&n))
102     {
103         memset(time,0,sizeof(time));
104         tsum = sum = 0;
105         for(int i=0; i<n; i++)
106         {
107             int t;
108             scanf("%d",&t);
109             time[t]++;
110             sum++;
111         }
112         top = 0;
113         for(int i=0; i<30; i++)
114         {
115             for(int j=i+1; j+i < 60; j++)
116             {
117                 if (check(i,j) == -1)
118                     continue ;
119                 line tmp;
120                 tmp.start = i;
121                 tmp.interval = j;
122                 tmp.times = (59-i)/j+1;
123                 lines[top++] = tmp;
124             }
125         }
126         sort(lines,lines+top);
127         ans = 17;
128         dfs(0,0);
129         printf("%d\n",ans);
130     }
131     return 0;
132 }