📜  门|门 CS 1996 |第 51 题(1)

📅  最后修改于: 2023-12-03 15:12:45.800000             🧑  作者: Mango

题目介绍

这是2019年清华大学计算机系B类机试的编程题目之一,题号为“门|门 CS 1996 |第 51 题”。该题目有一定难度,需要有一定的算法基础和编程经验才能解决。

题目描述

在一条直线上,有 n 个点(n<=1000),这些点从左到右标号为 0 ~ n-1,其中每个点都是响铃的门。现在需要修建补偿量等于 w 的办法。具体方法如下:

  1. 一开始所有门都关闭着(无法从外部打开)。
  2. 选择一个点i(0<=i<n),并支付费用w个单位,从外部为门i建立一个控制器,这样就可以从外部控制门i的开关状态。
  3. 在已经拥有控制器的门上,无需支付费用即可进行开关状态的改变(即从关到开或从开到关)。每次操作,只能改变其中一个门的状态。
  4. 你需要保证从左到右编号为 0 ~ n-1 的每个门,无论开与关,都可以在外部被打开。

你需要设计一个方案,使得最小化支付的费用 total,使得 total<=2000000。

题目分析

该题目是一道经典的贪心算法题目。我们可以借鉴哈夫曼编码的思想,从下到上依次构建控制器。首先需要对 n 个门按照初始状态(即未被控制前,门是否能从外部打开)进行排序,然后从n-1开始依次考虑,对于每个门i,如果它的状态是按在开着的,那么我们不需要为其建立控制器;如果它的状态是关着的,那么我们需要为其建立一个控制器,并将它的关状态转变为开。在具体实现该算法的过程中,可以使用一个布尔数组来记录每个门的状态,使用一个int数组记录每个门的代价。

主要代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int n,w;
int cost[MAXN];
bool door[MAXN];
int main(){
    cin>>n>>w;
    for(int i=0;i<n;i++){
        char ch;
        cin>>ch;
        door[i]=(ch=='1');
    }

    int ans=0;
    for(int i=0;i<n;i++){
        if(i==0||door[i]){
            ans+=cost[i];
        }
        else{
            int need=i;
            for(int j=0;j<=i;j++){
                if(door[j]&&need<=w){
                    need=j;
                    break;
                }
                if(door[j]){
                    need=min(need,j-w);
                }
                else{
                    need=max(need,j+w);
                }
            }
            if(need<=w) ans+=cost[i];
            door[need]=true;
            cost[need]=i+1;
        }
    }
    cout<<ans<<endl;
    return 0;
}
心得体会

这是一道相对来说比较难的贪心算法题目。在实现过程中,需要设计合适的数据结构来记录每个门的状态和代价。同时,还需要根据具体的情况来合理地选取数据结构进行求解。