poj1771-Elevator Stopping Plan

    技术2024-03-26  91

    照着网上的分析敲的代码,注释红字处为自己犯得小错误,以后尽量少犯。

    分析:可以将这题转换为可行性问题——对于时间t内,是否能到达所有的楼层。

    贪心法则: 基于一个楼层i,找到能控制它的最远停靠点j,然后再找j能控制的上边界 如果有乘客需要在第i层停靠,找到能走到i层的最高停靠点(因为可以从j下到i) 有临界方程 10cnt + (j-1) * 4 + (j-i)20 =t(cnt为之前已经停得次数) 解得基于i层,最高楼梯停靠j=(t-10cnt+20i+4)/24。

    然后,停在j层,这时乘客可以向上走,只要时间不超过t: 有临界方程(i-j)20+10cnt+(j-1)4 = t 。 得最高向上爬i=(t-10cnt+16*j+4)/20.

    总的来说,电梯停在某一层,在时间t内控制的是包含这个停靠的楼层的上下一段区域(以要到达的楼层为右边界)。

    对时间进行二分搜索。

    //2020/7/3 #include<iostream> using namespace std; #define Elevator 4//电梯上升一层楼所需时间 #define Stop 10 //电梯在一层楼中停留时间 #define Walk 20//人走一层楼所需时间 int n;//要去的楼层数 int cnt;//实际停的楼层数,**output要用,得是全局变量!!** int topfloor;//最高要停的楼层,作为分治法的边界 int floors[100] = { 0 };//记录每层楼是否要去 && **floor是向下取整函数**,不能做变量 int stop[100];//最终电梯停靠的楼层 int mid;//二分的中间数即最终答案 int judge(int tmp) {//判断tmp时间内能不能都到目的地 int i, t;/**/分清t和tmp,混了就很麻烦** //每次case重新初始化 cnt = 0; memset(stop, 0, sizeof(stop)); for (i = tmp/Walk+2;i<=topfloor; i++) {//为啥加二 while (!floors[i] && i < topfloor)//找到需要停的楼层 i++; t = Elevator * (i - 1) + Stop * cnt; if (t > tmp) return false; int j = (tmp - Stop * cnt + Walk * i + Elevator) / (Walk + Elevator); //方程得 能走到i的最高停靠层j i = (tmp - Stop * cnt + j * (Walk - Elevator) + Elevator) / Walk; //方程得 在j的基础上,向上能走到i stop[++cnt] = j; } return true; } int bisearch() {//对时间t进行二分 int L = 0; int R = (Stop + Elevator) * (topfloor - 1); while (L < R) { mid = (L + R+1) / 2; if (mid == R) break; if (judge(mid)) R = mid; else L = mid; } return mid;//返回出mid,给jugde再判断处理一下 } void output() { cout << mid << endl;//时间 cout << cnt;//停靠多少层 for (int i = 1; i <= cnt; i++) cout << " " << stop[i];//具体楼层 cout << endl; } int main() { int i,x; while (cin >> n, n) { memset(floors, 0, sizeof(floors));//**每次新的case都要memset重新初始化!!** for (i = 1; i <= n; i++) { cin >> x; floors[x] = 1; //由于输入是按照升序排列的,因此最后输入的楼层就是最高的 topfloor = x;//记录最高要停的楼层,作为分治法的边界 } judge(bisearch()); output(); } }
    Processed: 0.215, SQL: 9