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 }
时间: 2025-01-02 09:48:57