[2021.11.19]UPC-2021级新生个人训练赛第4场-19278 Problem D 关门

题目描述

为了将这些生产的玩具销往海外,晚上江北的玩具公司灯火通明。安安是公司的保安,当所有工作人员离开公司后,他要把公司里所有的门都关闭。房间的门有些是关闭的,有些是打开的。为了察看该公司里所有房间的门是否关闭,该公司安装了物联网智能门禁检测器,在保安配备的设备中会显示哪些房间的门已经关闭,哪些房间的门还是打开的。 
该公司共有 x 层,每层楼各有 y 个房间,每层楼的最左边和最右边各有一个楼梯。也就是说,这家公司可以表示为 x 行和 y+2 列的矩形,其中第一列和最后一列表示楼梯,中间 y列表示房间。(注意:最左边的是第一列楼梯)保安安安目前站在一楼的第一列的楼梯处。他必须一个房间一个房间去关门(注:关门在瞬间完成,所以关门时间可忽略不计),但安安只可以可用 1 单位时间完成以下内容之一:爬楼梯到达上一层楼,从房间到达相邻的房间或楼梯,从楼梯到达相邻的房间(也就是说向上、向左、向右“走一格”各需要 1 单位时间)。请你帮助安安计算关闭所有门的最短的单位时间。 
    特别提醒:安安是从最下面一层的第一列的楼梯处出发,他不必回到起始位置,并且他不用访问已经关门的房间。当所有房门都提示关闭时,他的任务就完成了。

输入

第一行输入两个整数 x 和 y,分别表示总共的楼层数和每层的房间数。 
下面的 x 行,每行输入 y + 2 个二进制的字符串,依次表示某个楼层的左楼梯,y 个房间,右楼梯,其中 0 表示门是关闭的,1 表示门是打开的。楼层是从下往上递增的,因此最后一行代表底层(就是第一层)。 
每行字符串的第一个和最后一个字符分别表示左侧楼梯和右侧楼梯,所以它们始终为 0。 

输出

输出一个整数,表示将所有门都关闭的最短时间。

样例输入 Copy

【样例1】
2 2 
0010 
0100 
【样例2】
3 4 
001000 
000010 
000010 
【样例3】
4 3 
01110 
01110 
01110 
01110 

样例输出 Copy

【样例1】
5
【样例2】
12
【样例3】
18
 

提示

样例一:安安先到达第一层楼的第一个房间,然后他可以使用左右两侧的楼梯到达第二层楼的第二个房间,看看使用哪个楼梯到达的时间最短,此样例使用左右两侧的楼梯的时间都相同。
样例二:安安先到达第一层楼的第四个房间,使用右侧楼梯到达第二层楼的第四个房间,再使用右侧楼梯,最后到达第三层楼的第二个房间。 
样例三:他将走过所有的房间,在每层楼的左右楼梯之间交替。 

1<=x<=15 且 1<=y<=100 

 笑着活下去  :)

这个题和第六场的打怪兽题都需要用到“动态规划”..最好是去百度一下,或者..
​​​​​​(3条消息) 【算法】详解动态规划_如风逝去-CSDN博客_动态规划

动态规划之背包问题系列 - 知乎 (zhihu.com)

用上面这两个链接简单了解一下~我就是通过上面两位大佬的讲解初步掌握了一点点.; )

关于这个题,学长也出了博客~:

关灯 - Wraith_Fiee的博客 (myqcloud.com)

了解完之后大家应该就都有能力解决这道题了~

不过 还是献丑分享一下我的代码(抄得学长的答案):

#include <algorithm>
#include <cstdio>
using namespace std;
int main(){
    int x,y,i,j,l[20],r[20]={0};
    scanf("%d %d",&x,&y);
    int a[20][110];  
    for(i=x;i>=1;i--){
        for(j=0;j<=y+1;j++)
            scanf("%1d",&a[i][j]);
    }
    fill(l,l+20,y+1);    

    //r[i]数组表示第i层最靠右的开门房间的位置 l[i]数组表示第i层最靠左的开门房间的位置

    int dp[20][2]={0};

    //dp[i][0]表示把从1~i层所有的门关上,然后回到i层左楼梯所需要的时间。
    //dp[i][1]表示把从1~i层所有的门关上,然后回到i层右楼梯所需要的时间。

    for(i=x;i>=1;i--){        
        for(j=0;j<=y+1;j++)
            if(a[i][j]==1){
                l[i]=min(l[i],j);
                r[i]=max(r[i],j);
            }
    }
    dp[1][1]=y+1;
    if(r[1]!=0){
        dp[1][0]=2*r[1];
    }
    int ans =r[1];    //ans=r[1]是为了解决输入中有x为1的特殊情况。
    for(i=2;i<=x;i++){
        if(r[i]!=0){    
            //如果该层有开着的门。
            ans=min(dp[i-1][0]+1+r[i],dp[i-1][1]+(y+1-l[i])+1);
            //ans表示从1~i层,把所有的门关掉所需的时间。
            dp[i][0]=min(dp[i-1][0]+1+2*r[i],dp[i-1][1]+y+1+1);
            dp[i][1]=min(dp[i-1][0]+(y+1)+1,dp[i-1][1]+1+(y+1-l[i])*2);
        }
        else{
            //如果该层没有开着的门,就直接上楼~
            dp[i][0]=dp[i-1][0]+1;
            dp[i][1]=dp[i-1][1]+1;
        }
    }
    printf("%d",ans);
}

如有纰漏,请多多指正!!!

PS:如果回去继续看那道题应该会看到红色加粗的三个字 “只可以”。

我自己不认真仔细,没读懂题意,还去跟棒棒糖说这题有错,老师就加粗了这三个字回复我。好羞愧,真想穿越回去扇自己一巴掌: ( 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值