【9.14NOIP模拟pj】wtaxi 题解——搜索

【9.14NOIP模拟pj】wtaxi

题目简化

有K辆车,N个人,上车给D元,只有S分钟。上车后无论多少人都要给D元,原地等多少分钟就没了多少元。求最小花费的钱。


我的思路

毫无疑问,此题可以用搜索做,DFS和BFS都行。

程序1

设三个参数,分别为(第l辆车,共花了sum元,还剩下m人)。花的时间在后面加上,否则有点麻烦一点。
两种情况:选或不选
不选 l+1;
选 l+1,已经花的钱+时间*上车人数+上车需要的钱(D),m-上车的人

程序2

我觉得,纯暴搜定然不过,所以加个记忆化。因为记忆化必须要在后面的操作一样时,才可以比较,取最优的。设bz[l,m]表示在第l辆车,剩m人时花的最少钱(sum)
和sum比较即可

程序3.1

我担心第二代程序过不了,再加了一层优化。有时,剩下的人即使将剩下的车全坐满,仍然不够。所以判断m和剩下的座位之和的关系,若m比其还大,则退出,因为即使后面的坐满,仍不能全走。

(未试过)程序3.2

已经满的车无需考虑


所以,此题可用**DFS+记忆化+剪枝**AC之

程序实践3.1

C++

#include <cstdio>
#include <cstring>
#include <cmath>
#include <climits>
using namespace std;
int n,k,d,s;                    
int t[101],z[101];                  
int bz[101][101],sm[102];//bz见上,sm[i]表示到i辆车时剩下的车最多能坐的人数(包括i)
int ans=INT_MAX;
int min_a_b(int a,int b)
{
    if (a<b) return a;
    return b;
}
void go(int l,int sum,int m)
{
    if (sum>bz[l][m]) return;
    bz[l][m]=sum;//记忆化操作,把大于之前的排除       
    if (sm[l]<m) return;//剪枝操作,把大于最大座位的人数排除
    if (m<=0) 
    {
        if (sum<ans) ans=sum;//替换ans
        return;
    }
    if (l>k) return;//车没了,人还在的情况
    go(l+1,sum+min_a_b(z[l],m)*t[l]+d,m-min_a_b(z[l],m));//选
    go(l+1,sum,m);//不选
    return;
}
int main()
{
    freopen("wtaxi.in","r",stdin);
    freopen("wtaxi.out","w",stdout);
    int i,j;
    scanf("%d%d%d%d",&n,&k,&d,&s);
    for(i=1;i<=k;i++)
    {
        scanf("%d%d",&t[i],&z[i]);
    }
    for(sm[k+1]=0,i=k;i>0;i--)
    {
        sm[i]=sm[i+1]+z[i];//求剩下车中最大座位
    }
    memset(bz,127,sizeof(bz));//初始化
    go(1,0,n);
    if (ans==INT_MAX) 
    {
        printf("impossible\n");
        return 0;
    }
    printf("%d\n",ans);
    return 0;
}

Pascal

var
        n,k,d,s:longint;
        i,j:longint;
        t,z:array[0..100]of longint;
        bz:array[0..100,0..100]of longint;//bz见上
        sm:array[0..101]of longint;//sm[i]表示到i辆车时剩下的车最多能坐的人数(包括i)
        ans:longint=maxlongint;
function min(a,b:longint):longint;
begin
        if a<b then exit(a);
        exit(b);
end;
procedure go(l,sum,m:longint);
begin
        if sum>bz[l,m] then exit;
        bz[l,m]:=sum;//记忆化操作,把大于之前的排除   
        if sm[l]<m then exit;//剪枝操作,把大于最大座位的人数排除
        if m<=0 then
        begin
                if sum<ans then ans:=sum;//替换ans
                exit;
        end;
        if l>k then exit;//车没了,人还在的情况
        go(l+1,sum+min(z[l],m)*t[l]+d,m-min(z[l],m));//选
        go(l+1,sum,m);//不选
end;
begin
        assign(input,'wtaxi.in');reset(input);
        assign(output,'wtaxi.out');rewrite(output);
        readln(n,k,d,s);
        for i:=1 to k do readln(t[i],z[i]);
        sm[k+1]:=0;
        for i:=k downto 1 do sm[i]:=sm[i+1]+z[i];//求剩下车中最大座位
        fillchar(bz,sizeof(bz),127);//初始化
        go(1,0,n);
        if ans=maxlongint then
        begin
                writeln('impossible');
                close(input);
                close(output);
                halt;
        end;
        writeln(ans);
        close(input);
        close(output);
end.

总结

  1. 类似于这样的题可以用搜索做
  2. 记忆化可以加快程序速度,但要保证程序是正确的,只是删掉不必要的运算
    1.不一定要保证前面走过的路一样
    2.要保证后面的路一样,它们要在同一可以比较的点上,才能记忆化,取最优的
  3. 剪枝也可以删掉一些不必要的运算,但它是根据最不利原则,求出的一个范围,把范围外的去掉
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值