2016_1_13(3)

点餐 

 (dinner.cpp/c/pas)  

【时间】1s

【问题描述】 

清儿今天请好朋友们吃饭,一共N个人坐在坐在圆桌旁。

吃饭的第一步当然是点餐了。服务员拿来了M份菜单。第i个人阅读菜单并点出自己喜欢的菜需要花费时间T[i]

当一个人点完菜之后,就会把菜单传到他右手边的第一个人。

M份菜单是同时发出的,每个菜单只能同时被一个人阅读。

清儿希望知道如何分发菜单,才能让点餐的总时间花费最少呢?

【输入】  

输入文件名为dinner.in

输入第一行是NM,表示人数和菜单数

输入第二行,N个数,表示每个人点餐所需要的时间。

【输出】 

输出文件名为dinner.out

输出一个整数表示点餐花费的最小时间。

【输入输出样例】

#1

3 2

1 5 10

 

10

 

#2

4 2

1 2 3 4

 

5

【数据说明】 

对于20%的数据,有1≤n≤100

对于60%的数据,有1≤n≤10000

对于100%的数据,有1≤n≤50000,T[i]<=600

 

这道题直接说了吧。我的做法是二分然后n^2 check,当时也想到了用倍增来优化那一个n^2,但是我比较蠢

只想到了mlogn的做法。然后正解是每次二分到一个ans之后处理出每一个点能到达的最远的点。然后跑一次倍增。

然后枚举起点,就可以logn check了。

总复杂度是nlogn^2

 

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <iostream>
 5 #include <cstring>
 6 using namespace std;
 7 const int N = 100003;
 8 int n,m,M = 1,cnt = 0;
 9 int a[2*N],l,r,maxna;
10 int ans = 2000000000;
11 int anc[2*N][30];
12 int st[3*N];
13 inline void prepare(int mid) {
14     int sum = 0;
15     int h = 0 ,t = 0;
16     for(int i = 1 ; i <= n+n ; ++i) {
17         st[++t] = i;
18         if(sum + a[i] <= mid) sum += a[i];
19         else {
20             do {
21                 anc[st[++h]][0] = i;
22                 sum -= a[st[h]];
23             }while(sum + a[i] > mid);
24             sum += a[i];
25         }
26     }
27     while(h<t) {
28         h++;
29         anc[st[h]][0] = 2*n+1;
30     }
31     for(int j = 0 ; j <= cnt ; ++j) anc[2*n+1][j] = 2*n+1;
32     for(int j = 1 ; j <= cnt ; ++j) {
33         for(int i = 1 ; i <= 2*n ; ++i) {
34             anc[i][j] = anc[anc[i][j-1]][j-1];
35         }
36     }
37 }
38 inline bool check(int mid) {
39     if(mid < maxna) return false;
40     prepare(mid);
41     for(int i = 1 ; i <= n ; ++i) {
42         int k = m;
43         int x = i;
44         for(int j = cnt ; j >= 0 ; --j) {
45             if((1<<j)<=k) {
46                 k -= (1<<j);
47                 x = anc[x][j];
48             }
49         }
50         if(x>=i+n) return true;
51     }
52     return false;
53 }
54 int main() {
55     freopen("dinner.in","r",stdin);
56     freopen("dinner.out","w",stdout);
57     scanf("%d%d",&n,&m);
58     while(m>=M) {
59         M<<=1;
60         cnt++;
61     }
62     cnt--;
63     for(int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]),r+=a[i],maxna = max(maxna,a[i]),a[i+n] = a[i];
64     do {
65         int mid = (l+r)>>1;
66         if(check(mid)) ans = min(ans,mid) , r = mid;
67         else l = mid + 1;
68     }while(l<r);
69     if(check(l)) ans = min(ans,l);
70     cout << ans << endl;
71 }
View Code

 

转载于:https://www.cnblogs.com/registerzxr/p/5127430.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值