題意:三種物品分別有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; }