The classical "poor cow" problem is described in C language

Posted by broann on Fri, 25 Feb 2022 02:36:48 +0100

The classical "poor cow" problem is described in C language

subject

Problem description

Farmer John has n (n ≤ 10000) cows. However, because they produced too little milk, the farmer was very dissatisfied with them and decided to make the least milk into beef jerky every day. But still a little reluctant, John plans to be merciful and let go of all the cows that day if more than one cow produces the least milk.

Since John's cows produce milk periodically, John can understand the ultimate fate of all cows from the beginning. But his math is very poor, so please help me figure out how many cows can survive in the end.

The milk production cycle Ti of each cow may be different, but it will not exceed 10 or less than 1. In each cycle, dairy cows produce no more than 100 milk per day.

Basic Requirements

The core of this problem is to take one minimum element at a time. However, because the elements change periodically, they cannot be directly organized into a heap. Since the periods are also different, it is only known that the maximum period does not exceed 10 days, so the simulated days may need at least 2520 days (this is the least common multiple of 1, 2,..., 10).

  1. Generate a cow parameter table, including the number of cycles per cow and their daily milk production during these cycles. Considering the large number, it is required to generate with random function.
  2. In days, simulate a reasonable time period (e.g. 2520) days to display the number of cattle slaughtered every day or amnesty cowshed because the same cattle produce the least milk.

Input and output

Input: the total number of cows, the number of days to be simulated, days.

Output: output the randomly generated cattle parameters to a text file in the format of

Cows with cycle 1: No		Day 1 (milk yield, the same below) 
				45		  34   
				97		  56   
				   ......	 ......   
Cows with cycle 2: No		Day 1	Day 2   
				243		   7	  39   
				651		  75	  37   
				 ......		 ......	 ......   

Then output the simulation results from day 1 (also need to output to another text file):

On the first day, the number of cattle with the least milk production is:		x No. 1 cattle were slaughtered or no cattle were slaughtered   
On the second day, the number of cattle with the least milk production is:		y No. 1 cattle were slaughtered or no cattle were slaughtered    
......   

Overall thinking

The description of this problem is very complex, but it probably means that a group of cows have to kill the cow with the smallest milk yield every day. Unless more than one cow has the same small milk yield on that day, it will not be killed. The change of milk yield of each cow has its own periodic law. Other descriptions are the definitions and descriptions of cattle, milk yield and cycle, which will not be repeated.

Through the above arrangement, we can get a simple algorithm idea: store the milk yield information of each cow and change it according to the cycle; The cow with the lowest milk yield shall be slaughtered every day and recorded; Update the herd and milk yield of each cow, and continue slaughtering until the simulation time stops.

Further, the whole process to be realized can be obtained: generating cattle, storing cattle, killing cattle, updating cattle and recording output cycle. The main program is as follows:

int main(void)    
{    
 int cowAmount = MAX_INDEX + 1;    
 int dayAmount = MIN_DAY;    // The above are the default values. See the following for the macro definition    
 getPara(&cowAmount, &dayAmount);    // Obtain the necessary parameters through user input    
 Cow *cows = initCow(cowAmount);    // Generate cow information    
 killCow(dayAmount, cows, cowAmount);   // The process of killing cattle    
 return 0;    
}

The key points and difficulties are: how to store cattle and update cattle data according to the cycle? How to efficiently find the one with the lowest milk yield?

Storage and update of cattle

According to the title description, there are three main data of cattle: number, cycle and milk yield. The milk yield changes according to the cycle, so it is necessary to record the value of each node in the cycle. Therefore, the author selects the generalized table and records the milk yield in the data structure of array. Specifically, in C language, the int pointer is used to record the address of the array:

typedef struct    
{    
 int index;    
 int* milk;    
 int cycle;    
} Cow;

Easy to get, the length of the milk yield array is the number of cycles of milk yield change, so you can use the date to get the remainder of the number of cycles of cattle, and use it as an index to obtain the value in the array.

cow.milk[(day - 1) % cow.cycle]    

It should be noted that the number of days starts from 1, the subscript of the array starts with 0, and the number of days needs - 1.

Get minimum using heap

principle

Heap data structure is commonly used in sorting. Heap sorting is an algorithm with the same time and space complexity as fast sorting. However, there is no need for perfect sorting in this problem, just take the minimum value, so just construct the heap, and make use of the characteristic that the child node of the heap must be smaller (larger) than the parent node. After sorting out the heap, compare whether the first several nodes are equal.

There are two writing methods of sorting heap: recursive and non recursive. The expression for obtaining the current output of cattle in this solution is slightly complex. If it is implemented in a non recursive way, it will make the already complex and difficult code worse. Therefore, it is implemented in a recursive way.

realization

/**    
 * @description: Exchange information about two cows    
 * @param {Cow} *cowA Niu A's address    
 * @param {Cow} *cowB Niu B's address    
 */    
void swapCow(Cow *cowA, Cow *cowB)    
{    
 Cow temp = *cowA;    
 *cowA = *cowB;    
 *cowB = temp;    
}    
    
/**    
 * @description: Recursively adjust the root node in the heap    
 * @param {Cow} list[] List address for storing cattle    
 * @param {int} root The position of the sub heap top in the list    
 * @param {int} length Length of cattle list (total length)    
 * @param {int} day Current days    
 */    
void adjustCowHeap(Cow list[], int root, int length, int day)    
{    
 Cow temp, *rootCow = &list[root];    
 if ((root + 1) * 2 < length) // has right child    
 {    
  Cow *rChild = &list[(root + 1) * 2];    
  Cow *lChild = &list[(root + 1) * 2 - 1];    
  if (lChild->milk[(day - 1) % lChild->cycle] < rChild->milk[(day - 1) % rChild->cycle])    
  {    
   if (lChild->milk[(day - 1) % lChild->cycle] < rootCow->milk[(day - 1) % rootCow->cycle])    
   {    
    swapCow(rootCow, lChild);    
    adjustCowHeap(list, (root + 1) * 2 - 1, length, day);    
   }    
  }    
  else    
  {    
   if (rChild->milk[(day - 1) % rChild->cycle] < rootCow->milk[(day - 1) % rootCow->cycle])    
   {    
    swapCow(rootCow, rChild);    
    adjustCowHeap(list, (root + 1) * 2, length, day);    
   }    
  }    
 }    
 else if ((root + 1) * 2 == length)    
 {    
  Cow *lChild = &list[(root + 1) * 2 - 1];    
  if (lChild->milk[(day - 1) % lChild->cycle] < rootCow->milk[(day - 1) % rootCow->cycle])    
  {    
   swapCow(rootCow, lChild);    
  }    
 }    
}    
    
/**    
 * @description: Initialize heap    
 * @param {Cow} list[] Cattle list    
 * @param {int} amount Length of list    
 * @param {int} day Current days    
 */    
void createCowHeap(Cow list[], int amount, int day)    
{    
 for (int i = (amount - 2) / 2; i >= 0; i--)    
 {    
  adjustCowHeap(list, i, amount, day);    
 }    
}

Detail implementation

How to determine whether to kill cattle on the same day

The condition of cattle killing is that only one cow has the least yield, because the use of pile organization data has made the cow array orderly to a certain extent, so it can be determined by comparing whether there are cows with the same yield near the top of the pile. To this end, the author has specially written an isKillable() function:

/**  
 * @description: Calculate the number of cows with the current minimum milk yield  
 * @param {Cow} list[] A sorted list of cattle  
 * @param {int} day Current days  
 * @return {int} Number of cows with current minimum milk yield  
 */  
int isKillable(Cow list[], int day)  
{  
 Cow head = list[0];  
 int i = 1;  
 while (1)  
 {  
  Cow temp = list[i];  
  if (head.milk[0] == MILK_OF_KILLED)  
  {  
   return 0;  
  }  
  if (head.milk[(day - 1) % head.cycle] != temp.milk[(day - 1) % temp.cycle])  
  {  
   return i;  
  }  
  i++;  
 }  
}

The function of this function is to return the one with the least output. Before judging, the function will first judge whether the cow on the top of the heap has been killed. If so, it means that the cow has been killed and there is no need to continue the comparison.

How to kill cattle

According to the storage mode and data structure of cattle and the characteristics of using heap to organize, the cattle killed are not set with special marks, but the cycle is changed to 1, and the milk yield is greater than the legal range required by the topic (the maximum value is 100, and the dead cattle in this topic is set to 999, see macro definition for details). Through this treatment, the dead cow will automatically "sink" from the top of the heap to the bottom of the heap when taking the minimum value to sort the heap, without additional deletion operations and occupying additional marking space.

  if (isKillable == 1)  
  {  
   Cow *killedCow = &list[0];  
   fprintf(  
    file, "The number of cattle producing the least milk is: %d\t\t%d Cattle were slaughtered\n",  
    killedCow->index, killedCow->index);  
   killedCowAmount++;  
   free(killedCow->milk);  
   killedCow->milk = (int *)malloc(sizeof(int));  
   killedCow->milk[0] = MILK_OF_KILLED;  
   killedCow->cycle = 1;  
  }  

Read input

Use placeholders to get the value returned by the scanf() function to eliminate compiler warnings. Use goto statement to automatically retry when users enter illegal content. Support random, easy to demonstrate:

int _ = 0; // Placeholder to eliminate scanf warnings    
/**    
 * @description: Get the number and days of simulated cattle    
 * @param {int} *cow Address for storing the number of cattle    
 * @param {int} *day Address of storage days    
 */    
void getPara(int *cow, int *day)    
{    
 int temp = 0;    
 srand((unsigned)time(NULL));    
 system("chcp 936 > NUL"); // Ensure that different devices display normally    
    
GETCOW:    
 puts(    
  "Please enter the total number of cattle. It is recommended to be greater than 200 and not greater than 10000."    
  "You can enter 0 to generate a random quantity:");    
 _ = scanf("%d", &temp);    
 if (temp == 0)    
 {    
  *cow = randint((MAX_INDEX + 1) / 5, MAX_INDEX + 1);    
 }    
 else if (temp <= 10000)    
 {    
  *cow = temp;    
 }    
 else    
 {    
  puts("Your input is incorrect, please try again!");    
  goto GETCOW;    
 }    
    
GETDAY:    
 puts(    
  "Please enter the number of days to simulate, which must not be less than 2520."    
  "You can enter 0 to randomly generate days:");    
 _ = scanf("%d", &temp);    
 if (temp == 0)    
 {    
  *day = randint(MIN_DAY, 5 * MIN_DAY);    
 }    
 else if (temp >= 2520)    
 {    
  *day = temp;    
 }    
 else    
 {    
  puts("Your input is incorrect, please try again!");    
  goto GETDAY;    
 }    
}

Macro definition

#include <stdio.h>    
#include <stdlib.h>    
#include <time.h>    
    
#define MIN_INDEX 0 	//  Cattle are numbered from 0    
#define MAX_INDEX 9999    
#define MIN_CYCLE 1    
#define MAX_CYCLE 10    
#define MIN_MILK 0    
#define MAX_MILK 100    
#define MILK_OF_KILLED 999 	//  The milk yield of cattle after death is set to 999, which is convenient for stacking and sorting    
#define MIN_DAY 2520    
#define COWS_FILE "cows.txt"    
#define DAYS_FILE "days.txt"  

Other modules

/**    
 * @description: Returns a random integer between the specified lower and upper limits    
 * @param {int} min Lower limit of random number size    
 * @param {int} max Upper limit of random number size    
 * @return {int} randomNum Pseudo random number in a given range    
 */    
int randint(int min, int max)    
{    
 int randomNum = 0;    
 randomNum = rand() % (1 + max - min) + min;    
 return randomNum;    
}    
    
/**    
 * @description: Initialize the list of cattle and format the output    
 * @param {int} amount Number of cattle    
 * @return {Cow*} Address of the array of cattle lists    
 */    
Cow *initCow(int amount)    
{    
 Cow *list = (Cow *)malloc(sizeof(Cow) * amount);    
 for (int i = 0; i < amount; i++)    
 {    
  Cow *cow = &list[i];    
  cow->index = i;    
  cow->cycle = randint(MIN_CYCLE, MAX_CYCLE);    
  cow->milk = (int *)malloc(sizeof(int) * cow->cycle);    
  for (int j = 0; j < cow->cycle; j++)    
  {    
   cow->milk[j] = randint(MIN_MILK, MAX_MILK);    
  }    
 }    
    
 FILE *file = fopen(COWS_FILE, "w");    
 for (int i = 1; i <= MAX_CYCLE; i++)    
 {    
  fprintf(file, "with a period of%d Cows: number  ", i);    
  for (int j = 1; j <= i; j++)    
  {    
   fprintf(file, "The first%d day\t", j);    
  }    
  fprintf(file, "\n");    
  for (int j = 0; j < amount; j++)    
  {    
   Cow cow = list[j];    
   if (cow.cycle == i)    
   {    
    fprintf(file, "\t\t\t\t%d\t", cow.index);    
    for (int k = 0; k < i; k++)    
    {    
     fprintf(file, "\t%d", cow.milk[k]);    
    }    
    fprintf(file, "\n");    
   }    
  }    
 }    
 fclose(file);    
    
 return list;    
}    
    
/**    
 * @description: Kill cattle and format output    
 * @param {int} days Total number of days simulated    
 * @param {Cow} list[] Cattle list    
 * @param {int} amount Number of cattle (including dead cattle)    
 */    
void killCow(int days, Cow list[], int amount)    
{    
 FILE *file = fopen(DAYS_FILE, "w");    
    
 int killedCowAmount = 0, noKillDays = 0;    
 for (int day = 1; day <= days; day++)    
 {    
  createCowHeap(list, amount, day);    
  fprintf(file, "The first%d God,", day);    
  int minCow = isKillable(list, day);    
  if (minCow == 1)    
  {    
   Cow *killedCow = &list[0];    
    
   fprintf(    
    file, "The number of cattle producing the least milk is: %d\t\t%d Cattle were slaughtered\n",    
    killedCow->index, killedCow->index);    
    
   killedCowAmount++;    
   free(killedCow->milk);    
   killedCow->milk = (int *)malloc(sizeof(int));    
   killedCow->milk[0] = MILK_OF_KILLED;    
   killedCow->cycle = 1;    
  }    
  else    
  {    
   fprintf(file, "The number of cattle producing the least milk is:");    
   for (int i = 0; i < minCow; i++)    
   {    
    fprintf(file, " %d", list[i].index);    
   }    
   fprintf(file, "\t\t No cattle were slaughtered\n");    
    
   noKillDays++;    
  }    
 }    
    
 fprintf(file, "Statistics: total slaughtered%d Cows, by the end of the simulation, a total of%d There was no slaughter of dairy cows in the past three days.", killedCowAmount, noKillDays);    
 fclose(file);    
}

Topics: C Algorithm