Name: The Buses
P_ID: POJ 1167
题目链接:http://poj.org/problem?id=1167
题意关键点:
1. 同一时刻可能有不同路线的车到达。这个要区分。
2. 每种路线的interval一定大于等于这条路线的第一辆车到达的时间。(强剪枝,因为这说明29分钟之后到的车都不可能是某条路线的第一辆车。)
题目分析:
这个题目的难点在于两个:
1. 建模
2. 剪枝
首先是建模。
一个比较明显的解决办法是dfs搜索。但是怎么建立模型比较麻烦。
我们可以基于路线建模。建一个名叫busRoute的struct,包含三个元素,begin, interval, times(这条路线中包括的车次)。建立结构数组,route[i]表示第i条路线。建立数组sumBus[i]记录第i分钟到达的车的数量。有了这些,我们可以开始读入数据并存储。
然后是剪枝。
我们首先要在route中存下所有可能包含的路线,注意,由于所有可能路线的第一辆车到达时间一定小于等于29,所以循环控制小于等于29(剪枝1)。所有可能包含的路线存储下之后,有一个很重要的剪枝工作,就是对这些所有可能的路线,按照其所包含的times从大到小进行排序(剪枝2)。如果当前已经确定的路线数量,加上假设剩下的没用过的车次都用下一个路线车次大小的路线(不一定存在)表示所需要的路线数量,就已经大于当前ans值,那么说明,这条路是死路,以后的不用做了,无用功。(类似POJ 1190)(剪枝3)
参考代码:
/**
*name: The Buses
*date: 2016-04-21
*note: dfs + modelling + pruning difficult
*/
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
struct busRoute {
int begin;
int interval;
int times;
};
busRoute route[1300];
int n, sumBus[1300];
int tot = 0, ans = 17;
bool comp(const busRoute a, const busRoute b)
{
return (a.times > b.times);
}
bool checkRoute(int a, int b)
{
for(int i=a; i<60; i+=b)
{
if(sumBus[i]==0)
return false;
}
return true;
}
//num表示目前已经确定的路线数量
void dfs(int a, int num)
{
if(n<=0)
{
if(num < ans)
ans = num;
return;
}
for(int k=a; k<tot; ++k)
{
if(num + n/(route[k].times) >= ans) return;//剪枝3
if(checkRoute(route[k].begin, route[k].interval))
{
int temp = route[k].interval;
for (int i = route[k].begin; i < 60; i += temp)//入栈
{
sumBus[i]--;
n--;
}
dfs(k, num + 1);//深搜
for (int i = route[k].begin; i < 60; i += temp)//回溯
{
sumBus[i]++;
n++;
}
}
}
}
int main()
{
scanf("%d", &n);
memset(sumBus, 0, sizeof(sumBus));
int bus;
for(int i=0; i<n; ++i)
{
scanf("%d", &bus);
sumBus[bus]++;
}
tot = 0;
for(int i=0; i<30; ++i)//剪枝1
{
if(sumBus[i]==0) continue;
for(int j=i+1; j<60-i; ++j)
{
if(checkRoute(i, j))
{
route[tot].begin = i;
route[tot].interval = j;
route[tot].times = (59-i)/j + 1;
tot++;
}
}
}
sort(route, route+tot, comp);//剪枝2
ans = 17;
dfs(0, 0);
printf("%d\n", ans);
return 0;
}