/* POTM ENTRY: mudpack */ /* Your Name: Doug Jones */ /* Your email: daj@esun.ho.att.com */ #include #include #include #include #include #define MAXN 200 #define MAXPSZ (MAXN/3+4) int nNumbers; /* number of numbered balls */ char triple[MAXPSZ][MAXPSZ][MAXPSZ]; /* !=0 if number triple covered */ int numCount[MAXPSZ]; /* counter used for tie breaking */ #define TRIPLE(N1,N2,N3) triple[N1][N2][N3] /* * Flags number triple N1,N2,N3 as covered. * Sets all permutations so that the code that checks doesn't have to. * I don't remember why the assignments aren't inside the if block. * Its probably a mistake since I added the if late in the development. */ #define SETTRIPLE(N1,N2,N3) \ if (triple[N1][N2][N3] == 0) { \ numCount[N1]++; \ numCount[N2]++; \ numCount[N3]++; \ } \ triple[N1][N2][N3]= 1; \ triple[N1][N3][N2]= 1; \ triple[N2][N1][N3]= 1; \ triple[N2][N3][N1]= 1; \ triple[N3][N1][N2]= 1; \ triple[N3][N2][N1]= 1; /* * Array used to renumber the tickets to reduce the final sum. */ struct count_ { int origNumber; int newNumber; int count; }; struct count_ countArray[MAXN+1]; struct ticket_ { int num[7]; /* usually the number minus one */ struct ticket_ *next; }; struct ticket_ *tickets = NULL; /* linked list of tickets */ struct ticket_ *unusedTickets = NULL; /* list of free ticket structures */ int nTickets; /* number of tickets on tickets list */ int numPartitionTickets[MAXPSZ]; /* number of tickets needed to */ /* completely cover a partition */ struct ticket_ *partitionTickets[MAXPSZ];/* tickets that cover a partition */ /* * Recycles the tickets on list. */ void freeTickets(struct ticket_ *list) { struct ticket_ *tp; while (list != NULL) { tp= list->next; list->next= unusedTickets; unusedTickets= list; list= tp; } } /* * Adds a new ticket to the tickets list and records all the * number triples that the ticket covers. */ void addTicket(int n1, int n2, int n3, int n4, int n5, int n6, int n7) { struct ticket_ *tp; if (unusedTickets == NULL) { tp= (struct ticket_ *) malloc(sizeof(struct ticket_)); } else { tp= unusedTickets; unusedTickets= unusedTickets->next; } tp->num[0]= n1; tp->num[1]= n2; tp->num[2]= n3; tp->num[3]= n4; tp->num[4]= n5; tp->num[5]= n6; tp->num[6]= n7; tp->next= tickets; tickets= tp; nTickets++; SETTRIPLE(n1,n2,n3); SETTRIPLE(n1,n2,n4); SETTRIPLE(n1,n2,n5); SETTRIPLE(n1,n2,n6); SETTRIPLE(n1,n2,n7); SETTRIPLE(n1,n3,n4); SETTRIPLE(n1,n3,n5); SETTRIPLE(n1,n3,n6); SETTRIPLE(n1,n3,n7); SETTRIPLE(n1,n4,n5); SETTRIPLE(n1,n4,n6); SETTRIPLE(n1,n4,n7); SETTRIPLE(n1,n5,n6); SETTRIPLE(n1,n5,n7); SETTRIPLE(n1,n6,n7); SETTRIPLE(n2,n3,n4); SETTRIPLE(n2,n3,n5); SETTRIPLE(n2,n3,n6); SETTRIPLE(n2,n3,n7); SETTRIPLE(n2,n4,n5); SETTRIPLE(n2,n4,n6); SETTRIPLE(n2,n4,n7); SETTRIPLE(n2,n5,n6); SETTRIPLE(n2,n5,n7); SETTRIPLE(n2,n6,n7); SETTRIPLE(n3,n4,n5); SETTRIPLE(n3,n4,n6); SETTRIPLE(n3,n4,n7); SETTRIPLE(n3,n5,n6); SETTRIPLE(n3,n5,n7); SETTRIPLE(n3,n6,n7); SETTRIPLE(n4,n5,n6); SETTRIPLE(n4,n5,n7); SETTRIPLE(n4,n6,n7); SETTRIPLE(n5,n6,n7); } /* * Prints the final list of tickets. */ void printTickets() { struct ticket_ *tp; for (tp=tickets; tp; tp=tp->next) printf("%d %d %d %d %d %d %d\n",tp->num[0],tp->num[1], tp->num[2],tp->num[3],tp->num[4],tp->num[5],tp->num[6]); } /* * Adds a copy of all tickets on list tp1 to the current tickets list. * Adds offset to all the numbers as they are copied. */ void cloneTickets(struct ticket_* tp1, int offset) { int i; struct ticket_ *tp2; for (; tp1; tp1=tp1->next) { if (unusedTickets == NULL) { tp2= (struct ticket_ *) malloc(sizeof(struct ticket_)); } else { tp2= unusedTickets; unusedTickets= unusedTickets->next; } for (i=0; i<7; i++) tp2->num[i]= tp1->num[i]+offset; tp2->next= tickets; tickets= tp2; } } /* * Makes a solution for the partition of size sz from the solution * of size sz+1 by replacing the extra number with any value allowed * (this happens when the final renumbering is done). * This only gets used for sz==14, since makePartition gets * a better answer for 15 than for 14. */ cloneBiggerPartition(int sz) { int i; struct ticket_ *tp1,*tp2; freeTickets(partitionTickets[sz]); partitionTickets[sz]= NULL; for (tp1=partitionTickets[sz+1]; tp1; tp1=tp1->next) { if (unusedTickets == NULL) { tp2= (struct ticket_ *) malloc(sizeof(struct ticket_)); } else { tp2= unusedTickets; unusedTickets= unusedTickets->next; } for (i=0; i<7; i++) tp2->num[i]= tp1->num[i]num[i] : -1; tp2->next= partitionTickets[sz]; partitionTickets[sz]= tp2; } numPartitionTickets[sz]= numPartitionTickets[sz+1]; } /* * Makes a solution for the partition of size sz from the solution * of size sz-1 by adding tickets that missing number with all pairs * of the other numbers in groups of three. * This only gets used for sz==16, which is good because it only * work if sz-1 is divisble by 3. */ extendSmallerPartition(int sz) { int i,j; freeTickets(partitionTickets[sz]); tickets= NULL; cloneTickets(partitionTickets[sz-1],0); nTickets= numPartitionTickets[sz-1]; for (i=0; i bc) { bs= s; bc= c; n4= i; n5= j; } } } } p14= triple[n1][n4]; p15= triple[n1][n5]; p24= triple[n2][n4]; p25= triple[n2][n5]; p34= triple[n3][n4]; p35= triple[n3][n5]; p45= triple[n4][n5]; bs= 0x7fffffff; for (i=0; i bc) { bs= s; bc= c; n6= i; n7= j; } } } } addTicket(n1,n2,n3,n4,n5,n6,n7); } } } if (nTickets < numPartitionTickets[sz]) { freeTickets(partitionTickets[sz]); numPartitionTickets[sz]= nTickets; partitionTickets[sz]= tickets; } else { freeTickets(tickets); } } /* * Tries to find the best solution for a partition of size sz * by running the greedy heuristic with various parameters. */ void makeBestPartition(int sz) { if (sz<7 || sz>=MAXPSZ) return; makePartition(sz,1,1,1,0); makePartition(sz,0,1,1,0); if (sz <= 40) makePartition(sz,0,0,1,0); if (sz <= 20) { makePartition(sz,0,0,0,0); makePartition(sz,1,1,-1,0); makePartition(sz,0,0,0,1); } } /* * Initializes the partition solution arrays and finds solutions * for partition sizes from nNumbers/3-4 to nNumbers/3+4. * Starts at nNumbers/3 and works its way outward. * Stops early if the run time gets close to the limit. */ void initPartitions() { int i,m; clock_t limit; struct tms t; for (i=0; i limit) break; makeBestPartition(m-i); times(&t); if (t.tms_utime+t.tms_stime > limit) break; } for (i=MAXPSZ-2; i>7; i--) if (partitionTickets[i]!=NULL && partitionTickets[i+1]!=NULL && numPartitionTickets[i]>numPartitionTickets[i+1]) cloneBiggerPartition(i); for (i=10; inumPartitionTickets[i-1]+m*(m-1)/2) extendSmallerPartition(i); } } /* * Comparison function used to sort countArray in decreasing count order. */ int countCompare(struct count_ *p1, struct count_ *p2) { return p2->count - p1->count; } /* * Comparison function used to sort countArray in origNumber order. */ int origCompare(struct count_ *p1, struct count_ *p2) { return p1->origNumber - p2->origNumber; } /* * Renumbers the tickets to reduce the sum. * Also assigns values to tickets that have don't care entries (<0). */ void minimizeTotal() { int i,j,k; struct ticket_ *tp; for (i=0; i<=nNumbers; i++) { countArray[i].origNumber= i; countArray[i].count= 0; } for (tp=tickets; tp; tp=tp->next) { for (i=0; i<7; i++) { if (tp->num[i] > nNumbers) tp->num[i]= 0; countArray[tp->num[i]].count++; } } qsort(countArray+1,nNumbers,sizeof(struct count_), (int(*)(const void *,const void *))countCompare); for (i=1; i<=nNumbers; i++) countArray[i].newNumber= i; countArray[0].newNumber= 0; qsort(countArray+1,nNumbers,sizeof(struct count_), (int(*)(const void *,const void *))origCompare); for (tp=tickets; tp; tp=tp->next) { for (i=0; i<7; i++) tp->num[i]= countArray[tp->num[i]].newNumber; for (i=0; i<7; i++) { if (tp->num[i] == 0) { for (j=1; j<7; j++) { for (k=0; k<7; k++) if (tp->num[k] == j) break; if (k == 7) break; } tp->num[i]= j; } } } } main(int argc, char**argv) { int p1,p2,p3,sz,bestsz,bestp1,bestp2,bestp3; nNumbers= atoi(argv[1]); if (nNumbers > 21) { /* * Divide nNumbers into three parts and generate * tickets to completely cover each part. * Then loop over all ways to partition nNumbers into * three to find the best one. */ initPartitions(); bestsz= 0x7fffffff; for (p1=7; p1=MAXPSZ) continue; sz= numPartitionTickets[p1] + numPartitionTickets[p2] + numPartitionTickets[p3]; if (sz < bestsz) { bestsz= sz; bestp1= p1; bestp2= p2; bestp3= p3; } } } tickets= NULL; cloneTickets(partitionTickets[bestp1],1); cloneTickets(partitionTickets[bestp2],1+bestp1); cloneTickets(partitionTickets[bestp3],1+bestp1+bestp2); minimizeTotal(); printTickets(); } else if (nNumbers > 16) { printf("1 2 3 4 5 6 7\n"); printf("8 9 10 11 12 13 14\n"); printf("%d %d %d %d %d %d %d\n", nNumbers-6,nNumbers-5,nNumbers-4,nNumbers-3, nNumbers-2,nNumbers-1,nNumbers); } else if (nNumbers > 11) { printf("1 2 3 4 5 6 7\n"); printf("%d %d %d %d %d %d %d\n", nNumbers-6,nNumbers-5,nNumbers-4,nNumbers-3, nNumbers-2,nNumbers-1,nNumbers); } else { printf("1 2 3 4 5 6 7\n"); } exit(0); }