黑书1.5.2例题2 公路巡逻
首先对于时刻的处理,我们以六点整为0s,把所有时刻都换成s。然后根据最大和最小速度以及10Km,我们可以知道目标车从i关口到i+1关口需要300~600s的时间,到达i关口的最小时间为(i-1)*300,最大时间为(i-1)*600.我们设k为相邻关口行驶时间的可能数目(300),则最大时间是O(nk)的,用dp[i][t]表示在时间t到达关口i,与巡逻车最少相遇的次数,则
dp[i][t]=min{dp[i−1][t−k]+count(i−1,t−k,t)|300≤k≤600}
.
count(i,sti,edi)表示目标车在sti时刻从i关口出发,edi时刻到达i+1关口,途中会遇到几次巡逻车。我们设i关口的每辆巡逻车st时刻出发,ed时刻到达i+1关口。则
1.
ed==edi一定在终点相遇
2.
st<sti
&&edi<=ed,巡逻车出发早却到的晚,一定在途中被超车
3.
st>sti
&&edi>=ed,巡逻车出发晚却到的早,一定在途中超车了
所以count可以在平均
O(m/n)
的时间内求出。所以状态数为
O(kn2)
,决策为O(k),转移时间为O(m/n),总的时间复杂度为
O(k2nm)
.
#include <cstdio>
#include <cstring>
#define inf 0x3f3f3f3f
#define N 50
int n,m,st[N][300],ed[N][300],dp[N][30000];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
inline int trans(int x){
return (x/10000-6)*3600+x%10000/100*60+x%100;//060000-->0
}
inline int count(int i,int sti,int edi){
int res=0;
for(int j=1;j<=st[i][0];++j){
if(edi==ed[i][j]) res++;
else if(st[i][j]<sti&&edi<=ed[i][j]) res++;
else if(st[i][j]>sti&&edi>=ed[i][j]) res++;
}
return res;
}
int main(){
// freopen("a.in","r",stdin);
bool blank=0;
while(~scanf("%d%d",&n,&m)){
if(blank) puts("");else blank=1;
memset(st,0,sizeof(st));
while(m--){
int x=read(),t=read(),y=read();
st[x][0]++;st[x][st[x][0]]=trans(t);
ed[x][st[x][0]]=st[x][st[x][0]]+y;
}
memset(dp,0x3f,sizeof(dp));
dp[1][0]=0;
for(int i=2;i<=n;++i){
int lt=(i-1)*300,rt=(i-1)*600;
for(int t=lt;t<=rt;++t)
for(int k=300;k<=600;++k)
dp[i][t]=min(dp[i][t],dp[i-1][t-k]+count(i-1,t-k,t));
}
int ans=inf,ansid=-1;
for(int t=(n-1)*300;t<=(n-1)*600;++t)
if(dp[n][t]<ans) ans=dp[n][t],ansid=t;
printf("%d\n%06d\n",ans,(ansid/3600+6)*10000+ansid%3600/60*100+ansid%60);
}
return 0;
}