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 }

poj 1167 DFS,布布扣,bubuko.com

时间: 2025-01-02 09:48:57

poj 1167 DFS的相关文章

POJ 1167 (DFS&#183;剪枝&#183;公交时刻表)

题意  你记录了[0, 59]这个时间段内到达你所在站牌的所有公交的到这个站牌的时间  对于每路公交 1. 同一路公交的到站时间间隔是相同的 2. 每路公交在这个时间段至少到达两次 3. 最多有17路公交 4. 两个不同路的公交的第一次到站时间和到站时间间隔都可能是相同滴 5. 你在这个时间段内的记录是完整的 求最少用多少路公交可以让你的记录合法 由于每路公交至少到站两次  那么第一次到站时间是肯定小于30的  而且到站时间间隔肯定要大于第一次到站的时间  那么可以根据你的记录生成所有可能合法的

POJ 3321 DFS序+线段树

单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4:   5: #include <cstdio> 6: #include <cstring> 7: #include <cctype> 8: #include <algorithm> 9: #include <vector> 10: #include <iostream> 1

Oil Deposits(poj 1526 DFS入门题)

http://poj.org/problem?id=1562 Oil Deposits Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 12595   Accepted: 6868 Description The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp wor

poj 1069 DFS+剪枝

1 /* 2 题意:给出一个边长为S的六边形,再给出n种边长不同的三角形,所有的长度均为整型,问这n种三角形是否 3 能够拼成这个六边形. 4 5 题解:DFS+剪枝 6 这题的关键是图的表示方法以及剪枝,图我用了一个二维数组直接表示: 7 111111111111111111111 8 111110000000000011111 9 111100000000000001111 10 111000000000000000111 11 110000000000000000011 12 100000

poj 2488 dfs

背景:就是简单的遍历全图搜索,但要注意两点: 1.一开始以为起点不同会有不同结果,所以就枚举了起点,但实际上只要能遍历全图就能把A1作为起点,因为遍历全图就是每个点都要走到,那么A1也要走到,既然可以走到A点,那么也可以从A点走到其它点. 2.题目中的字典序输出,不看看很能想到题意是先满足列,然后满足行,这样写出满足条件的方向数组即可. //poj 2488 #include<map> #include<set> #include<stack> #include<

poj 1190 DFS 不等式放缩进行剪枝

F - (例题)不等式放缩 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 1190 Description 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱.当i < M时,要求Ri &

poj 1979 dfs

水过,注意边界不能超出. #include <iostream> using namespace std; int n, m, sx, sy, dir[4][2] = {0, -1, 0, 1, 1, 0, -1, 0}, count; char diagram[23][23]; void get_diagram(void) { for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> diag

Channel Allocation (poj 1129 dfs)

Language: Default Channel Allocation Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 12367   Accepted: 6325 Description When a radio station is broadcasting over a very large area, repeaters are used to retransmit the signal so that ever

poj 3009 dfs

背景:dfs,再加点模拟,各种代码疏漏错误wa了三次!!也有变量名使用不规则照成的.比如临时变量我我就应该用temp,buffer,key,三个变量名来表示. 思路:每一个点四个方向的dfs,到达终点就判断最少步数. bfs的思路:这个是经典的最短路问题,但是缺点是,地图会改变而bfs没办法像dfs那样容易回溯,方法就是把地图直接放在每一个坐标上,也就是定义一个结构体: struct place{ int x,y,step; int diagram[M][M];//每一个坐标点都付一个图 } 我