Google Code Jam 2016 Round 1C C

题意:三种物品分别有a b c个(a<=b<=c),如今每种物品各选一个进行组合。要求每种最和最多出现一次。且要求任意两个物品的组合在全部三个物品组合中的出现总次数不能超过n。ide

要求给出一个方案,使得咱们可以生成的组合数最多。spa

分析:code

首先咱们能够简单的处理一种状况,就是c<=n的状况。blog

由于咱们枚举出了全部组合,那么两物品的出现次数的最大值不会超过c,由于A种和B种的每对组合都会在其中出现c次,其他两个的组合出现次数更少。it

因此这种状况必定不会超过n,咱们只须要枚举全部组合便可。io

 

然而,对于c>n的状况,若是咱们对每一个AB物品对枚举n次,event

咱们必须合理分配如何给这些AB对指定对应的C物品,不然AC或者BC物品对就有可能超越n次。因此这个问题不是很简单就能解决的。class

不管怎么枚举,咱们的总组合数不可能超过a×b×n。不然必然致使某些AB物品的出现次数超过n。cli

又由于a b都小于c,因此a*b*n就是咱们所求答案的上界。sed

 

如今有一个构造方法能够保证对于任意的c>n的状况,构造出一种a*b*n的合法组合数。

方法就是构造一个长宽高分别是abc的由1×1×1小正方体堆叠而成的立方体,其中每一个小正方体其坐标都对应了一个三物品组合,

咱们要从中选出最多的正方体来。

而n的限制则能够形象表示为每x、y、z轴方向的行中选中的正方体数量都不能超过n。

咱们先来构造a=1的这个平面的正方体。在b=1这一行的c个正方体里,咱们选取1~n(n<c)的正方体加入答案。

而随着b的增加,咱们将这个选取区间依次向右平移。b=x时选x~(n+x)%c。因为b<c因此这个区间最多完成一次平移循环。

对于a=1这个平面里,每行每列都不会超过n个被选中的。

 

接下来咱们来构建每一个b=x的平面,有b个这样的平面。

对于每一个平面,方法与以前相同,对于a=1的一行咱们选择1~n区间,随着a增加,区间也进行平移。

对于a=y这一行,选择区间为y~(n+y)%c。与以前同理,必定合法。

 

如今保证了全部c方向的行中,选中数量小于等于n。a方向的行中选中数量小于等于n。可是b方向的行中,咱们只保证了在a=1这个平面内的小于等于n。

可是不用担忧,由于其他的b方向的是合法的,由于咱们的构建方法,至关于将a=1这个平面随着a的增加而向b方向进行平移。

 

这样就构建完成了,找到这样构建的被选中正方体的坐标规律,输出便可。

#include <cstdio>
#include <algorithm>
using namespace std;

int a, b, c, n;

void work()
{
    if (c <= n)
    {
        printf("%d\n", a * b * c);
        for (int i = 1; i <= a; i++)
            for (int j = 1; j <= b; j++)
                for (int k = 1; k <= c; k++)
                    printf("%d %d %d\n", i, j, k);
        return;
    }
    printf("%d\n", a * b * n);
    for (int i = 0; i < a; i++)
        for (int j = 0; j < b; j++)
            for (int k = i + j; k < i + j + n; k++)
            {
                    printf("%d %d %d\n", i + 1, j + 1, k % c + 1);
            }
}

int main()
{
    int t;
    scanf("%d", &t);
    int case_num = 0;
    while (t--)
    {
        case_num++;
        printf("Case #%d: ", case_num);
        scanf("%d%d%d%d", &a, &b, &c, &n);
        work();
    }
    return 0;
}
View Code
相关文章
相关标签/搜索