Description
A man arrives at a bus stop at 12:00. He remains there during 12:00-12:59. The bus stop is used by a number of bus routes. The man notes the times of arriving buses. The times when buses arrive are given.
- Buses on the same route arrive at regular intervals from 12:00 to 12:59 throughout the entire hour.
- Times are given in whole minutes from 0 to 59.
- Each bus route stops at least 2 times.
- The number of bus routes in the test examples will be <=17.
- Buses from different routes may arrive at the same time.
- Several bus routes can have the same time of first arrival and/or time interval. If two bus routes have the same starting time and interval, they are distinct and are both to be presented.
Find the schedule with the fewest number of bus routes that must stop at the bus stop to satisfy the input data. For each bus route, output the starting time and the interval.
Input
Your program is to read from standard input. The input contains a number n (n <= 300) telling how many arriving buses have been noted, followed by the arrival times in ascending order.
Output
Your program is to write to standard output. The output contains one integer, which is the fewest number of bus routes.
Sample Input
17
0 3 5 13 13 15 21 26 27 29 37 39 39 45 51 52 53
Sample Output
3
题意 你记录了[0, 59]这个时间段内到达你所在站牌的所有公交的到这个站牌的时间 对于每路公交
1. 同一路公交的到站时间间隔是相同的
2. 每路公交在这个时间段至少到达两次
3. 最多有17路公交
4. 两个不同路的公交的第一次到站时间和到站时间间隔都可能是相同滴
5. 你在这个时间段内的记录是完整的
求最少用多少路公交可以让你的记录合法
对于最多有17路公交,告诉我们最多只有17层,
于是我们可以限制层数,当合法时直接输出
避免搜到没有必要的东西
这就叫迭代加深
于是代码构架:
void dfs(int Maxdeep(限制的层数),Num(当前有几辆车),Rest(还剩多少个时刻没有匹配)
{
if(Num>Maxdeep) 如果合法,输出退出, 否则返回//迭代加深的思想
剪枝***
枚举下一辆车
}
一些优化:
for(int i=1;i<30;i++){
if(cnt[i]){
for(int j=i+1;j<60;j++){
if(ok(i,j)){
...
dfs(...)
...
}
}
}
}
我开始是这样枚举下一辆的,放在dfs里面非常费时间
但我们可以先处理好,然后直接用
我们用a[i].F a[i].T a[i].C 表示第i辆车第一次出现的时间,间隔,次数
处理好之后
枚举变成了这样
for(int i=1;i<=k;i++){//k为合法的总个数
if(!vis[i]&&ok(a[i].F,a[i].J)){
vis[i]=1;
for(int j=a[i].F;j<=59;j+=a[i].J) cnt[j]--;
dfs(Maxdeep,Num+1,Rest-a[i].C);
for(int j=a[i].F;j<=59;j+=a[i].J) cnt[j]++;
vis[i]=0;
}
}
这样的好处大大的,比如说原来O(MN)的复杂度现在变成了O(M) + O(N)
所以我们可以先预处理一些状态,dfs时直接用
另外,我们发现,每次尽量选次数多的
预处理完这些状态后,按次数从大到小排序就可以满足
可以直接从Num开始枚举
进一步,排好序后
如果(Maxdeep-Num+1)*a[i].C <= Rest
即剩下可以公交车总数,乘上最多的次数,都到不了Rest 很明显不合法
于是枚举变成了这样
for(int i=Num;i<=k;i++){
if((Maxdeep-Num+1)*a[i].C<Rest) return;
if(!vis[i]&&ok(a[i].F,a[i].J)){
vis[i]=1;
for(int j=a[i].F;j<=59;j+=a[i].J) cnt[j]--;
dfs(Maxdeep,Num+1,Rest-a[i].C);
for(int j=a[i].F;j<=59;j+=a[i].J) cnt[j]++;
vis[i]=0;
}
}
总结一下本题思路:
1.迭代加深限制层数,一旦搜到就是最优解
2.先在外面预处理可行的状态
3.贪心,尽量去次数最多的车
4.如果最优解都不合法,直接返回
#include<iostream>
#include<algorithm>
#define N 70
using namespace std;
int cnt[N],n,k,flag,vis[N];
struct Node{int F,J,C;}a[N];//起始时间,间隔,次数
bool ok(int i,int j){
while(i<60){if(cnt[i]==0) return false;i+=j;}
return true;
}
bool cmp(Node a,Node b){return a.C>b.C;}
void dfs(int Maxdeep,int Num,int Rest){
if(Num>Maxdeep){if(Rest==0){cout<<Maxdeep;exit(0);}return;}
for(int i=Num;i<=k;i++){
if((Maxdeep-Num+1)*a[i].C<Rest) return;
if(!vis[i]&&ok(a[i].F,a[i].J)){
vis[i]=1;
for(int j=a[i].F;j<=59;j+=a[i].J) cnt[j]--;
dfs(Maxdeep,Num+1,Rest-a[i].C);
for(int j=a[i].F;j<=59;j+=a[i].J) cnt[j]++;
vis[i]=0;
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int t;cin>>t;cnt[t]++;
}
for(int i=0;i<30;i++)
for(int j=i+1;j<=59-i;j++)
if(ok(i,j)) a[++k].F=i,a[k].J=j,a[k].C=(59-i)/j+1;
sort(a+1,a+k+1,cmp);
for(int i=1;i<=17;i++){
dfs(i,1,n);
}
}