COGS 612 摩托车游戏

612. 摩托车游戏

★☆   输入文件: carz.in   输出文件: carz.out    简单对比
时间限制:1 s   内存限制:128 MB

[问题描述]

晚会上大家在玩一款“暴力摩托”的游戏,它拥有非常逼真的画面和音响效果,如疾驰而过的汽车呼啸声,摩托车的引擎声和转弯时轮胎与地面摩擦而产生的声音。而且它在游戏中加入了对抗成份,比赛中你可以使用拳、脚去干扰对方,使其落后于你,是不是很卑鄙啊 ? 游戏中千万不能手下留情,因为对手会主动攻击你。如果遇到开摩托车的警察,虽然也可以对他踢上一脚,但可得小心点呀,万一被他们捉住了,那就 GAME OVER 啦!

当然了,车子总是要加油的咯,已知赛道长 S公里(S≤10000整数,且为10的倍数),赛车的油耗Q=1,即 1公里 路耗 1个单位的油。Q不变,赛车的油箱为无穷大,同时在沿途的任何地方都可以加油。 约定,每次加油的数量为整数,且为 10的倍数,赛车的速度与赛车加油后的总油量有关。其关系如下表列出:

加油量

车速(公里 / 小时)

  ≤10

100

(10,20 ]

90

(20,30 ]

80

(30,40 ]

75

(40,+∞)

70

 

同时,汽车每加油一次需要耗费 T分钟(T<=100不论加油多少,开始时的加油不计时间)

当 S,T给出之后,选择一个最优的加油方案。使汽车以最少时间跑完全程。

例如:当 S=40,T=6(分钟),加油的方案有许多种,列出一些:

1)起点加油40,用时40/75≈0.53小时

2)起点加油20,中途加20,用时20/90+20/90+6/60(化为小时)≈ 0.54 小时

[输入文件]

一行,为两个整数 S、T。

[输出文件]

输出一行,为 最少用时(保留二位小数)

[输入样例]

40 6

[输出样例]

0.53



话说这个题又让我和机房的小伙伴们重温了一遍暴力摩托(下载下来重温(-.-))

这个题呢 一开始想的是搜索

但是当时思维比较混乱 写着写着就wa了

不过后来还是改A了

但是好像还存在问题

先贴代码吧

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct self{int oil;double v,start,cost;}s[6];
int m,n,a,b,c,d;
double z=9999999;

void dfs(int dis,double time)
{
    int a;
    if(dis<0)return;
    if(dis==0){z=min(z,time);return;}
    if(time>z)return;
    for(a=1;a<=5;a++)
    {
        if(dis>=s[a].oil)
        {
            for(b=1;;b++)if(b*s[a].oil>dis)break;
            b--;
            dfs(dis-s[a].oil*b,time+b*s[a].cost);
        }
        else
        dfs(0,time+n+(double)dis/s[a].start);
    }
}
    

int main()
{
    freopen("carz.in","r",stdin);
    freopen("carz.out","w",stdout);
    scanf("%d%d",&m,&n);
    
    s[1].oil=10;s[1].start=100;
    s[2].oil=20;s[2].start=90;
    s[3].oil=30;s[3].start=80;
    s[4].oil=40;s[4].start=75;
    s[5].oil=99999;s[5].start=70;
    
    for(a=1;a<=5;a++)if(m<=s[a].oil)z=min(z,(double)m/s[a].start);
    
    for(a=1;a<=5;a++)s[a].v=(double)s[a].oil/(s[a].oil/s[a].start+(double)n/60);
    for(a=1;a<=5;a++)s[a].cost=s[a].oil/s[a].v;
    
    for(a=1;a<=5;a++)
    if(m>=s[a].oil)dfs(m-s[a].oil,s[a].oil/s[a].start);

    printf("%.2lf\n",z);
    return 0;
}
s数组表示每次加油的信息

oil表示加油上限(每次肯定加到这个上限)

v表示 跑oil公里(即加oil油)除以时间(加油时间+跑路时间)所得的速度

start表示从0出发时的速度

cost表示加油和跑路的总时间


结果一开始超时

后来写成每次都加oil 走到终点前距离<oil时分类讨论下 就过了

不过感觉这样写还是有问题的 



后来想了个DP

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m;double n;
double f[1011];
int a,b,c;

int main()
{
    freopen("carz.in","r",stdin);freopen("carz.out","w",stdout);
    cin>>m>>n;
    m/=10;n/=60;
    
    f[1]=1.0/10;
    f[2]=min(2.0/9,f[1]+f[1]+n);
    f[3]=3.0/8;f[3]=min(f[3],f[1]+2.0/9+n);f[3]=min(f[3],f[2]+1.0/10+n);
    f[4]=4.0/7.5;f[4]=min(f[4],f[3]+1.0/10+n);f[4]=min(f[4],f[2]+2.0/9+n);f[4]=min(f[4],f[1]+3.0/8+n);
    for(a=5;a<=m;a++)
    {
        f[a]=a/7.0;
        f[a]=min(f[a],f[a-1]+1.0/10+n);
        f[a]=min(f[a],f[a-2]+2.0/9+n);
        f[a]=min(f[a],f[a-3]+3.0/8+n);
        f[a]=min(f[a],f[a-4]+4.0/7.5+n);
    }
    
    printf("%.2lf\n",f[m]);
    return 0;
}
    
f[i]表示走到i的最小花费

因为走到i肯定是油0了 所以此后加油方案与f[i]无关

 因为每次加油 和 路程都是10的倍数 所以一开始都/10 以减小空间和时间


其实一开始想到的是N^2的转移

for(a=5;a<=m;a++)
{
    f[a]=a/7.0;
    for(b=1;b<=a-1;b++)
    {
        if(b==a-1)f[a]=min(f[a],f[b]+1.0/10+n);
        if(b==a-2)f[a]=min(f[a],f[b]+2.0/9+n);
        if(b==a-3)f[a]=min(f[a],f[b]+3.0/8+n);
        if(b==a-4)f[a]=min(f[a],f[b]+4.0/7.5+n);
        if(b<=a-5)f[a]=min(f[a],f[b]+(a-b)/7.0+n);
    }
}

也可以AC



但是我蒟蒻一开始想到的就是由5个状态更新当前状态

其实以5个状态更新的也是对的

这两个的区别就是 为何不能从中间的一段加无穷大的油跑到终点

假如从一开始不加无穷大的油 停在了oil处

这样的时间比以无穷大的油跑到的时间短

那么此后我们仍每次加oil油 时间肯定还是比以无穷大的油跑到的时间短

我们就不需要从中间加正无穷的油了


另外也可以当成 从出发点也需要加油的时间

最后时间减去加油时间即可

这样一想好像上面的证明错了

因为一开始是没有加油时间的 即不加油以oil的油速度跑时间<不加油以正无穷的油速度跑时间小

不一定 

不加油以oil的油速度跑时间+加油时间<不加油以正无穷的油速度跑时间小



那么这个应该对了如果加油oil时间+跑的时间小于加油正无穷+跑

那么加油oil时间+跑的时间小于(加油正无穷)跑

那么这样应该对了= =

感觉还是有一点问题。。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m;double n;
double f[1011];
int a,b,c;

int main()
{
    freopen("carz.in","r",stdin);freopen("carz.out","w",stdout);
    cin>>m>>n;
    m/=10;n/=60;
    
    f[1]=1.0/10+n;
    f[2]=min(2.0/9+n,f[1]+f[1]);
    f[3]=3.0/8+n;f[3]=min(f[3],f[1]+f[2]);
    f[4]=4.0/7.5+n;f[4]=min(f[4],f[3]+f[1]+n);f[4]=min(f[4],f[2]+f[2]);
    for(a=5;a<=m;a++)
    {
        f[a]=a/7.0+n;
        f[a]=min(f[a],f[a-1]+f[1]);
        f[a]=min(f[a],f[a-2]+f[2]);
        f[a]=min(f[a],f[a-3]+f[3]);
        f[a]=min(f[a],f[a-4]+f[4]);
    }
    printf("%.2lf\n",f[m]-n);
    return 0;
}



反正N^2的时肯定对的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值