您的位置:首页 > 其它

乘电梯还是走楼梯

2009-09-30 13:32 267 查看
http://acm.zjgsu.edu.cn/JudgeOnline.html

Description


blue 新开了一家公司,开张第一天,他邀请ACMers一起去玩。ACMers一定吓了一大跳,因为大厦有31层高。

所以大厦里肯定有电梯。每个ACMer都有自己想去的楼层,现在blue想,这些人要从大厦底层到达各自的目标楼层,

可以通过搭乘电梯,也可以走楼梯,每种方式都有固定的上楼所需时间,而电梯在每层停靠的时间也是固定的。

现在blue希望ACMers想出一个最佳的上楼方案,使得最后一个到达其目标楼层的人所花的时间最小。

现在规定:大厦有31层楼,乘电梯上(下)一楼要花4秒时间,电梯停靠需要花10秒时间,走楼梯上(下)

一楼需要花20秒时间。那么从1楼到31楼,如果乘电梯并且不停靠需要花(31-1)×4=120秒时间,如果每层都

停需要花30×4+29×10=410秒时间(在31楼没有必要停靠了),如果走楼梯的话需要花30×20=600秒时间。

假设ACMers需要在4,5,10楼停靠。那么最佳停靠方案为电梯在4,10楼停靠。因为电梯在4楼停靠需要花3×4

=12秒,然后停靠花费10秒,然后到达10楼需要花费3×4+10+6×4=46秒。那些想去4楼的ACMers需要花费12秒,那些

想去5楼的ACMers需要花费12+20=32秒,那些想去10楼的ACMers需要花费46秒。因此最后一个到达他的目标楼层最小

花费46秒。

Input


测试包括多组数据。

每组测试数据为一行,形如:n,f1,f2,f3,...fn (n<=30,2<=f1,f2,...,fn<=31)。

n代表总共有n楼电梯需要停靠,f1,f2,...fn是这些要停靠的楼层。

当n输入为0时结束。

Output


对应每组测试数据,输出一行,最后一个ACMer到达其目标层的最少时间。

Sample Input


3 4 5 10

1 2

0


Sample Output


46

4


有动态规划的代码但是看不懂,在另外一本书上看到是二分枚举加贪心,核心就两个公式,很像分形,还是看不懂。

照着贪心的思想自己写了一个,一直WA到凌晨,第二天吃中饭的时候终于发现犯了一个低级错误,终于。。。

再回过头来看那两个公式,原来就是把我的代码最简化.

#include <iostream>
#define MAXN 30001
using namespace std ;
int a[MAXN] , n ;
int Cmp (const void *e1 , const void *e2)
{
return *(int *)e1 - *(int *)e2 ;
}
bool Solved (int t)
{
int i , j , stops ;
for (i = 0 ; i < n && (a[i] - 1) * 20 <= t ; i ++) ;
for (stops = 0 , j = 1 ; i < n ; stops ++)
{
if ((a[i] - 1) * 4 + stops * 10 > t)
return false ;
for (j = a[i] ; j <= a[n - 1] && (j - a[i]) * 20 + (j - 1) * 4 + 10 * stops <= t ;)
j ++ ;
if (j > a[n - 1])
break ;
j -- ;
while (i < n && (a[i] - j) * 20 + (j - 1) * 4 + 10 * stops <= t)
i ++ ;
if (i == n)
break ;
}
return true ;
}
int main ()
{
int ans , low , i , high , mid ;
while (scanf ("%d" , &n) , n)
{
memset (a , 0 , sizeof (a)) ;
for (i = 0 ; i < n ; i ++)
scanf ("%d" , &a[i]) ;
qsort (a , n , sizeof (int) , Cmp) ;
low = 0 ;
high = 10000 ;
while (low <= high)
{
mid = (low + high) >> 1 ;
if (Solved (mid))
{
ans = mid ;
high = mid - 1 ;
}
else
low = mid + 1 ;
}
printf ("%d/n" , ans) ;
}
return 0 ;
}


这是书上的代码

#include <iostream>
#define MAXN 40
using namespace std ;
int a[MAXN] , n , bound ;
bool Solved (int t)
{
int i , j , stops ;
i = t / 20 + 2 ;
for (stops = 0 ; i <= bound ; stops ++)
{
while (i <= bound && !a[i])
i ++ ;
if ((i - 1) * 4 + stops * 10 > t)
return false ;
j = (t - 10 * stops + 20 * i + 4) / 24 ;
//这个代码对应着我的第一个循环,在t的范围内,j表示要停的最高层数
i = (t - 10 * stops + 16 * j + 4) / 20 + 1 ;
//这个代码对应着我的第二个循环,在j层停后,第i层以下都能在t的范围内达到目标层数
}
return true ;
}
int main ()
{
int ans , low , i , high , mid , tmp ;
while (scanf ("%d" , &n) , n)
{
memset (a , 0 , sizeof (a)) ;
for (i = bound = 0 ; i < n ; i ++)
{
scanf ("%d" , &tmp) ;
if (tmp > bound)
bound = tmp ;
a[tmp] = 1 ;
}
low = 0 ;
high = 1000 ;
while (low <= high)//二分枚举答案
{
mid = (low + high) >> 1 ;
if (Solved (mid))
{
ans = mid ;
high = mid - 1 ;
}
else
low = mid + 1 ;
}
printf ("%d/n" , ans) ;
}
return 0 ;
}


用<=通过累加算边界时,可以用/代替,

终于弄懂动态规划的解法了。

#include <iostream>
#include <algorithm>
#define INF 1000
using namespace std ;
int max (int a , int b)
{
return a > b ? a : b ;
}
int min (int a , int b)
{
return a < b ? a : b ;
}
int main ()
{
int f[35][35] , a[35] , i , j , k , walk , stops , n ;
while (scanf ("%d" , &n) , n)
{
for (i = 0 ; i < n ; i ++)
scanf ("%d" , &a[i]) ;
sort (a , a + n) ;
for (i = 0 ; i < 35 ; i ++)
for (j = 0 ; j < 35 ; j ++)
f[i][j] = INF ;
for (i = a[n - 1] ; i > 0 ; i --)
for (j = n - 1 ; j >= 0 ; j --)
{
f[i][j] = f[i + 1][j] + 4 ;
for (walk = 0 , k = j ; k < n ; k ++)
{
walk = max (walk , 20 * abs (a[k] - i)) ;
if (k < n - 1)
{
stops = (i > 1 ? 10 : 0) ;
f[i][j] = min (f[i][j] , max (f[i + 1][k + 1] + stops + 4 , walk)) ;
}
else
f[i][j] = min (f[i][j] , walk) ;
}
}
printf ("%d/n" , f[1][0]) ;
}
return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: