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 }