【代码源每日一题Div2】饿饿 饭饭「二分答案」

饿饿 饭饭

题目描述:

n个同学,每个人需要打a[i]份饭,排队打饭,每个在队头的人才能打饭,且打完一份饭就会到队尾去,打够a[i]份饭后就离开了,现在食堂一共只有K份饭,问最后的队伍序列长什么样,你只需要输出剩下的没走的人在原序列的id,按当前结束的队伍排列进行输出

思路:

二分答案

显然我们可以二分出需要进行多少轮打饭能打完K份饭

check也很好写,求一个 ∑ i = 1 n m i n ( m i d , a [ i ] ) \sum_{i=1}^{n}{min(mid,a[i])} i=1nmin(mid,a[i]),跟K进行判断就行

如果无论如何都无法打完K份饭,则是-1

否则,我们可以先通过打K-1份饭得到进行最后一轮打饭之前的人的情况,用一个队列存下来,然后模拟一遍最后一轮的过程就行

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, x;
int tr[MAX];

bool judge(int mid){
    
    
    int num = 0;
    for(int i = 1; i <= n; ++i){
    
    
        num += min(mid, tr[i]);
    }
    return num >= k;
}

void work(){
    
    
    cin >> n >> k;
    for(int i = 1; i <= n; ++i)cin >> tr[i];
    int l = 0, r = k;
    while(l <= r){
    
    
        int mid = (l + r) / 2;
        if(judge(mid))r = mid - 1;
        else l = mid + 1;
    }
    if(l == k + 1)cout << -1 << endl;
    else{
    
    
        vector<int>a;
        ll sum = 0;
        queue<pii>q;
        for(int i = 1; i <= n; ++i){
    
    
            k -= min(tr[i], l - 1);
            tr[i] = max(0ll, tr[i] - l + 1);
            if(tr[i])q.push(m_p(i, tr[i]));
        }
        k -= sum;
        while(k--){
    
    
            auto x = q.front();q.pop();
            --x.second;
            if(x.second)q.push(x);
        }
        while(!q.empty()){
    
    
            cout << q.front().first << ' ';
            q.pop();
        }
        cout << endl;
    }
}


signed main(){
    
    
    io;
    work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_51216553/article/details/127761663