sgu 542 Gena vs Petya

 

 

sgu 543 Cafe

题意:有N组人需要被分配到某些固定了人数的桌子上,其中ai表示第i组有多少个人,安排作为需要符合如下安排:某一组的人员不能够单独在一张桌子,每张桌子坐的人数不能够超过其限制,问至少要安排多少张给定了人数限制的桌子。

分析:此题是一个贪心题,如果直接求解的话,由于桌子的数量是未知的,因此不太好分配,因此二分桌子的数量,在已知桌子的数量后运用贪心规则来求得最优解。但尽管在二分已知桌子的情况下还是不太好实现这个贪心规则。其思路是这样的:
1.如果桌子的限制为奇数,那么想办法把这些的桌子用奇数人数的组分出1个3来填,使得其限制变成偶数,这样是为了更好的填满奇数限制的桌子;
2.如果没有奇数人数的组,那么找出总人数大于等于6的组,分出尽可能多的6出来,把这个6分成2个3来填奇数限制的桌子,总而言之就是尽可能使得奇数限制的桌子能够被奇数的人数恰好填满;
3.处理完桌子后,就是统计各组人数的情况,如果还有奇数组,那么拿出一个3作为3人组,其余均变为2人组,最后统计一共有多少个3人组多少个2人组,这样做之后就使得不同组能够统一处理;
4.最后就是把这些3人组以及2人组分配到修正后的桌子上,先成对成对的放置3人组,这样能够使得尽可能的按照偶数分配,因为如果存在三人组,那么修正的桌子中一定不存在奇数限制的桌子(否则肯定会被奇数组的人填),不能成对分配之后再单个单个的分配,最后再分配2人组,如果都能够放下则说明二分的这个解成立,否则不成立。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

const int N = 2005;
int n, r;
int a[N], b[N];
struct E {
    int vol, num;
    E(int x, int y): vol(x), num(y) {}
};
vector<E>v;

bool Ac(int M) { // 判定M张桌子能否完成安排
    int x, y; // x表示容量为偶数的桌子数量,y表示容量为奇数的桌子数量 
    if (r & 1) x = 0, y = M;
    else x = M, y = 0;
    memcpy(b, a, sizeof (a));
    v.clear();
    // 以下用来使得桌子尽可能多的转化为容量为偶数的桌子 
    if (r & 1) {
        for (int i = 0; i < n && y; ++i) { // 首先将奇数人中的3填到奇数容量的桌子中 
            if (b[i] & 1) {
                b[i] -= 3, y -= 1, x += 1;
            }
        }
        for (int i = 0; i < n && y >= 2; ++i) { // 只有当所有的奇数组人数都匹配后且奇数桌有剩余时才会执行此处 
            while (b[i] >= 6 && y >= 2) {
                b[i] -= 6, y -= 2, x += 2;
            }
        }
        // 如果还有奇数容量的桌子就一定没有奇数组的人或者是组员数大于6的偶数组 
        // 如果还有奇数个组员的组就一定没有奇数容量的桌子 
        if (x) v.push_back(E(r-3, x));
        if (y) v.push_back(E(r, y));
    } else {
        v.push_back(E(r, x));
    }
    // 接下来求剩下各组人数的奇偶情况,p2、p3用来表示被分成2人组合3人组的组数 
    int p3 = 0, p2 = 0;
    for (int i = 0; i < n; ++i) {
        if (b[i] & 1) {
            b[i] -= 3, p3 += 1;
        }
        p2 += b[i] / 2;
    }
    // 先开始放置偶数对3人组,是因为这样不会使得桌子容量退化为奇数
    for (int i = 0; i < v.size() && p3 >= 2; ++i) {
        if (v[i].vol >= 6) {
            int t1 = v[i].vol / 6; // 每个剩余的偶数桌子能够容纳多少对3
            int t2 = min(v[i].num, p3/(2*t1)); // 已有的3人组能够占用多少张该桌子
            if (t2 > 0) {
                p3 -= t2*t1*2;
                v[i].num -= t2;
                v.push_back(E(v[i].vol-6*t1, t2));
            }
            if (v[i].num > 0 && p3 >= 2) {
                t2 = min(t1, p3/2);
                if (t2 > 0) {
                    p3 -= t2*2;
                    v[i].num -= 1;
                    v.push_back(E(v[i].vol-6*t2, 1));
                }
            }
        }
    }
    // 能够减掉偶数对3人组已经排除,因此只剩下一次减掉一次三人组的情况 
    for (int i = 0; i < v.size() && p3 > 0; ++i) {
        if(v[i].vol >=3 && v[i].num > 0) {
            int tmp = min(v[i].num, p3);
            p3 -= tmp;
            v[i].num -= tmp;
            v.push_back(E(v[i].vol-3, tmp));
        }
    }
    if (p3 <= 0) {
        for (int i = 0; i < (int)v.size() && p2 > 0; ++i) {
            p2 -= (v[i].vol/2)*v[i].num;
        }
    }
    return p3 <= 0 && p2 <= 0;
}

int main() {
    while (scanf("%d %d", &n, &r) != EOF) {
        int ret;
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
        }
        int l = 1, r = n * 2000;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (Ac(mid)) {
                ret = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        printf("%d\n", ret);
    }
    return 0;
}
View Code

相关文章:

  • 2021-10-23
  • 2021-06-29
  • 2021-07-09
  • 2021-09-01
  • 2022-03-04
  • 2021-08-23
  • 2021-06-13
猜你喜欢
  • 2021-05-31
  • 2021-11-10
  • 2021-11-03
  • 2022-01-21
  • 2021-09-14
  • 2021-07-19
  • 2022-02-20
相关资源
相似解决方案