bzoj2538 [Ctsc2000]公路巡逻

博客介绍了Ctsc2000公路巡逻问题,其中一辆目标车从第1个关口出发,需要计算在一定速度限制下,与巡逻车最少相遇次数。巡逻车按特定时刻和速度从不同关口出发。文章提供了输入输出样例,并讨论了解题思路,涉及动态规划和剪枝策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(http://www.elijahqi.win/2018/01/07/bzoj2538-ctsc2000%E5%85%AC%E8%B7%AF%E5%B7%A1%E9%80%BB/)
题目描述
在一条没有分岔的高速公路上有n个关口,相邻两个关口之间的距离都是10km。所有车辆在这条高速公路上的最低速度为60km/h,最高速度为120km/h,并且只能在关口处改变速度。巡逻的方式是在某个时刻Ti从第ni个关口派出一辆巡逻车匀速驶抵第(ni+1)个关口,路上耗费的时间为ti秒。
两辆车相遇是指它们之间发生超车或者两车同时到达某关口(同时出发不算相遇)。
巡逻部门想知道一辆于6点整从第1个关口出发去第n个关口的车(称为目标车)最少会与多少辆巡逻车相遇,请编程计算之。假设所有车辆到达关口的时刻都是整秒。
输入输出格式
输入第一行为两个用空格隔开的整数,分别为关口数n和巡逻车数m。

输出格式:

输出第一行为目标车与巡逻车相遇次数。第二行为目标车与巡逻车相遇次数最少时最早到达第n个关口的时刻(格式同输入中的Ti)。

输入输出样例
输入样例#1: 复制
3 2
1 060000 301
2 060300 600
输出样例#1: 复制
0
061301
说明
每个测试点1s
ctsc2000t第一试

洛谷一直不开o2跑不过 于是加了点小的剪枝就okay了
设f[i][j]表示到达第i号位置花费时间为j 遇到的最少的巡逻车的数量是多少
那么我如果从前面转移过来的话 因为有这个60km/h和120km/h的限速 所以最多是300s~600s这个区间内 所以我每次只需要去枚举一下我是由前面区间中哪个转移过来即可 剪枝就是 如果已经为0了 那么直接退出就好

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 330
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}return x;
}
int st[55][N],ed[55][N],n,m,dp[55][40000],cnt[55];
inline int change(int x){return (x/10000-6)*3600+(x/100-x/10000*100)*60+x%100;}
inline int calc(int pos,int st1,int ed1){
    int tmp=0;
    for (int i=1;i<=cnt[pos];++i){
        if(ed[pos][i]==ed1) {tmp++;continue;}
        if (st1>st[pos][i]&&ed1<ed[pos][i]) {tmp++;continue;}
        if (st1<st[pos][i]&&ed1>ed[pos][i]) {tmp++;continue;}
    }return tmp;
}
int main(){
//  freopen("1499.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=m;++i){
        int start=read(),time=read(),time_last=read();
        st[start][++cnt[start]]=change(time);ed[start][cnt[start]]=st[start][cnt[start]]+time_last;
    }memset(dp,0x3f,sizeof(dp));dp[1][0]=0;
    for (int i=2;i<=n;++i){
        int l=(i-1)*300,r=(i-1)*600;
        for (int j=l;j<=r;++j){
            for (int k=300;k<=600;++k){
                dp[i][j]=min(dp[i][j],dp[i-1][j-k]+calc(i-1,j-k,j));
                if (!dp[i][j]) break;
            }
        }
    }int l=(n-1)*300,r=(n-1)*600,ans=inf,time=0;
    for (int i=l;i<=r;++i) {
        if (dp[n][i]<ans) ans=dp[n][i],time=i;
    }printf("%d\n",ans);printf("%02d%02d%02d",6+time/3600,time%3600/60,time%60);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值