International Olympiad in Informatics
1992 Bonn / Germany

Authors:

 Peter Heyderhoff (Editor),
Hans-Werner Hein,
Fritz Krückeberg,
Günther Miklitz,
Peter Widmayer



Solutions

The First Problem

Day 1: "Islands in the Sea"

The problem can be recognized as simular to the well konwn two person game "Destroy Ships". The objects asked for are very similar but the information gaining is different.

The problem is solved using recursion and backtracking. The procedure find_maps assigns an island mark or an empty mark to the actual field and calls itself recursively in order to assign to the next field. If all fields could be assigned successfully according to the given constraints, the map will be shown and the recursion will be backtracked by restoring the old values. By this exhausting recursion and backtracking process it is garanted that all solutions will be found.

Description of the program:

The algorithm is described in a semi formal manner:

          read the data;
          solve the problem.
        
          read the data:        
            for all rows
              read data and check its correctness.

          solve the problem:
            initialize pointers to the the field;
            start with the first field;
            search map.

          search map:   
            if map complete
               draw map;
            if empty mark will fit to the current field
               assign empty mark;
               next field;
               search map;
               backtrack restoring the old value;
            if island mark will fit     to the current field
               assign island mark;
               next field;
               search map;
               backtrack restoring the old value.
Protokoll of a run:
                4
                2 0
                2 0
                2 0
                2 0
                2 0
                2 0
                2 0
                2 0

                  1 2 3 4
                1     * *
                2     * *
                3 * *
                4 * *

                  1 2 3 4       
                1 * *
                2 * *
                3     * *
                4     * *

Program in Pascal

PROGRAM IOI92_Day_1_Islands_in_the_sea;
{ Author: Matej Ondrusek, CZ }
 USES crt;

 TYPE line=ARRAY[0..5] OF integer;
        { line of input file }
 VAR inp,out:text;      { input,output file }
     n:integer; { number of rows/collums }
     row,col:ARRAY[1..8] OF line;
        { rows, columns information }
     rp,cp,rs,cs:ARRAY[1..8] OF integer;
        { r/c pointer, r/c start }
     map:ARRAY[1..8,1..8] OF integer;  { map of sea }
     nomap:boolean;     { no map can be found }
        { at least one map found }

 { Read one block of input file }

 PROCEDURE readinput;
  VAR i,j:integer;

  { If there is some mistake in input file ... }

  PROCEDURE eiif;
  BEGIN
   clrscr;
   writeln(#7,'Error in input file !');
   halt
  END;

  { Read one line of informations from input line }

  PROCEDURE readline(VAR a:line; VAR s:integer);
   VAR j:integer;
  BEGIN
   a[0]:=-1;
   s:=n+2;
   j:=1;
   IF eoln(inp) THEN eiif;
   read(inp,a[j]);
   WHILE a[j]>0 DO
    BEGIN
     IF j=5 THEN eiif;
     write(a[j],' ');
     s:=s-1-a[j];
     j:=j+1;
     IF eoln(inp) THEN eiif;
     read(inp,a[j]);
    END;
   writeln('0');
   readln(inp);
   IF s=n+2 THEN s:=n+1;
   IF a[j]<0 THEN eiif;
   IF s<1 THEN nomap:=true;
  END;

 BEGIN
  IF eoln(inp) THEN eiif;
  readln(inp,n); writeln(n);
  IF (n>8) OR (n<1) THEN
   BEGIN
    writeln('N is out of range !');
    halt;
   END;
  FOR i:=1 TO n DO readline(row[i],rs[i]);
  FOR i:=1 TO n DO readline(col[i],cs[i]);
  readln(inp);
  IF ioresult<>0 THEN eiif;
  {$i+}
  writeln; writeln('Press any key to continue.'); writeln;
  WHILE readkey=#0 DO;
 END;

 { Write map to the screen and to the output file }

 PROCEDURE write_map;
  VAR i,j:integer;
 BEGIN
  IF found THEN writeln(out);
  found:=true;
  write('  ');
  FOR i:=1 TO n DO write(i,' '); writeln;
  FOR i:=1 TO n DO
   BEGIN
    write(i,' ');
    FOR j:=1 TO n DO
     IF map[i,j]=1
     THEN BEGIN write('* '); write(out,'* '); END
     ELSE BEGIN write('  '); write(out,'  '); END;
    writeln; writeln(out);
   END;
  writeln; writeln('Press any key to continue.');
  writeln;
  WHILE readkey=#0 DO;
 END;

 { Recurrent procedure for reconstructing maps.
   It puts space or island on the r/c position
   (if possible) and calls itself for next position }

 PROCEDURE find_maps(r,c:integer);
  VAR rc,cc:boolean;
 BEGIN
  IF c=n+1 THEN
   BEGIN
    r:=r+1; c:=1;
   END;
  IF r=n+1 THEN
   BEGIN
    write_map;
    exit;
   END;

  { Try to put space on this position }

  IF ((row[r,rp[r]]=0) OR (row[r,rp[r]]=-1) AND
     (rs[r]>c)) AND
     ((col[c,cp[c]]=0) OR (col[c,cp[c]]=-1) AND
     (cs[c]>r)) THEN
   BEGIN
    map[r,c]:=0; rc:=false; cc:=false;
    IF row[r,rp[r]]=0 THEN
       BEGIN inc(rs[r]); rc:=true; row[r,rp[r]]:=-1
       END;
    IF col[c,cp[c]]=0 THEN
       BEGIN inc(cs[c]); cc:=true; col[c,cp[c]]:=-1
       END;
    find_maps(r,c+1);
    IF rc
    THEN BEGIN row[r,rp[r]]:=0; dec(rs[r]) END;
    IF cc
    THEN BEGIN col[c,cp[c]]:=0; dec(cs[c]) END;
   END;

  { Try to put island on this position }

  IF ((row[r,rp[r]]>0) OR (row[r,rp[r]]=-1) AND
     (rs[r]<=n)) AND
     ((col[c,cp[c]]>0) OR (col[c,cp[c]]=-1) AND
     (cs[c]<=n)) THEN
   BEGIN
    map[r,c]:=1; rc:=false; cc:=false;
    inc(rs[r]); inc(cs[c]);
    IF row[r,rp[r]]=-1
    THEN BEGIN rc:=true; inc(rp[r]) END;
    IF col[c,cp[c]]=-1
    THEN BEGIN cc:=true; inc(cp[c]) END;
    dec(row[r,rp[r]]); dec(col[c,cp[c]]);
    find_maps(r,c+1);
    inc(row[r,rp[r]]); inc(col[c,cp[c]]);
    IF rc THEN dec(rp[r]);
    IF cc THEN dec(cp[c]);
    dec(rs[r]); dec(cs[c]);
   END;

 END;

 { This is the main one-problem solving procedure }

 PROCEDURE solveproblem;
  VAR i:integer;

 BEGIN
  writeln(out,'next problem');
  FOR i:=1 TO n DO
   BEGIN
    rp[i]:=0; cp[i]:=0;
   END;
  found:=false;
  IF not nomap THEN find_maps(1,1);
  IF not found THEN
   BEGIN
    writeln('No map!'); writeln(out,'no map');
    writeln; writeln('Press any key to continue.');
    WHILE readkey=#0 DO;
   END;
 END;

BEGIN
 assign(inp,'c:\ioi\day-1\413-seas.in'); reset(inp);
 assign(out,'c:\ioi\day-1\413-seas.ou');
 rewrite(out);

 { This is the main repetition
   - UNTIL the input file is read completely }

 WHILE not eof(inp) DO
  BEGIN
   nomap:=false;
   clrscr;
   readinput;
   solveproblem;
  END;

 close(inp);
 close(out);

END.

Program in C

/***************************************************

  Shawn Smith   7/15/92   Day 1  "Islands in the Sea"
****************************************************/

#include<stdio.h>

#define SEASIN "413-SEAS.IN"
#define SEASOU "413-SEAS.OU"
#define bputc(ch) (putchar(ch),putc(ch,fileout))
#define bprintf(str)
 (printf(str),fprintf(fileout,str))
#define PAUSE()
 ( printf("hit any key for the next problem\n"),  \
                  getch() == 'q' ? exit(0) : 0)
#define placespace(xpos,ypos,xislpos,spaceleft) \
        (spaceleft == 0 || left[xpos][ypos] > 0 ? 0 :    \
            searchrow(xpos+1,ypos,xislpos,spaceleft-1))

int xisls[5][9],numxisls[9],yisls[5][8],
    numyisls[8],gridsize;
int curyisl[8][9],left[8][9],spaces[9];
int successcount;
FILE *filein,*fileout;

main()
{
  if((filein = fopen(SEASIN,"r")) == NULL)
    printf("error opening %s\n",SEASIN),exit(1);
  if((fileout = fopen(SEASOU,"w")) == NULL)
    printf("error opening %s\n",SEASOU),exit(1);

  while(readblock() == 0) {
    collectrowstats();
    initfirstrow();
    successcount = 0;
    searchrow(0,0,0,spaces[0]);
    if(successcount == 0)
      bprintf("no map\n");
    PAUSE();
  }

  fclose(fileout);
  fclose(filein);
}

readblock()
{
  int i,j,count;

  if(fscanf(filein,"%d",&gridsize) == EOF)
    return -1;
  bprintf("next problem\n");

  for(i=0; i < gridsize; i++) {
    fscanf(filein,"%d",&xisls[0][i]);
    for(count=0; xisls[count][i] > 0; count++)
      fscanf(filein,"%d",&xisls[count+1][i]);
    numxisls[i] = count;
  }
  numxisls[gridsize] = 0;
  for(i=0; i < gridsize; i++) {
    fscanf(filein,"%d",&yisls[0][i]);
    for(count=0; yisls[count][i] > 0; count++)
      fscanf(filein,"%d",&yisls[count+1][i]);
    numyisls[i] = count;
  }

  printf("grid size: %d\n",gridsize);
  for(i=0; i < gridsize; i++) {
    printf("row #%d:",i+1);
    for(j=0; j < numxisls[i]; j++)
      printf(" %d",xisls[j][i]);
    printf(" 0\n");
  }
  for(i=0; i < gridsize; i++) {
    printf("col #%d:",i+1);
    for(j=0; j < numyisls[i]; j++)
      printf(" %d",yisls[j][i]);
    printf(" 0\n");
  }
  return 0;
}

collectrowstats()
{
  int i,j,sum;

  for(i=0; i <= gridsize; i++) {
    for(sum=j=0; j < numxisls[i]; j++)
      sum += xisls[j][i];
    spaces[i] = gridsize-sum;
  }
}

initfirstrow()
{
  int i;

  for(i=0; i < gridsize; i++) {
    left[i][0] = -1;
    curyisl[i][0] = 0;
  }
}

searchrow(int xpos,int ypos,int xislpos,
          int spaceleft)
{

  if(xpos == gridsize && spaceleft > 0) return;
  else if(xpos == gridsize && ypos < gridsize) {
    propagaterow(ypos);
    searchrow(0,ypos+1,0,spaces[ypos+1]);
  }
  else if(xpos == gridsize) success();
  else if(xislpos == numxisls[ypos] ||
       left[xpos][ypos] == 0)
    placespace(xpos,ypos,xislpos,spaceleft);
  else if(left[xpos][ypos] > 0)
    placeisland(xpos,ypos,xislpos,spaceleft);
  else {
    placeisland(xpos,ypos,xislpos,spaceleft);
    placespace(xpos,ypos,xislpos,spaceleft);
  }
}

placeisland(int xpos,int ypos,int xislpos,
            int spaceleft)
{
  int i,changed = 0;

  for(i=0; i < xisls[xislpos][ypos]; i++,xpos++)
    if(xpos == gridsize || left[xpos][ypos] == 0 ||
       curyisl[xpos][ypos] >= numyisls[xpos])
      goto restore;
    else if(left[xpos][ypos] == -1) {
      left[xpos][ypos] =
        yisls[curyisl[xpos][ypos]][xpos];
      changed |= 1 << i;
    }
  if(xpos < gridsize)
    placespace(xpos,ypos,xislpos+1,spaceleft);
  else
    searchrow(xpos,ypos,xislpos+1,spaceleft);

restore:
  for(i--,xpos--; i >= 0; i--,xpos--)
    if((changed >> i) & 1)
      left[xpos][ypos] = -1;
}

/*
placespace(int xpos,int ypos,int xislpos,
           int spaceleft)
{
  if(spaceleft > 0 && left[xpos][ypos] <= 0)
    searchrow(xpos+1,ypos,xislpos,spaceleft-1);
}
*/

propagaterow(int ypos)
{
  int i;

  for(i=0; i < gridsize; i++) {
    left[i][ypos+1] = left[i][ypos] > 0 ?
    left[i][ypos]-1 : -1;
    curyisl[i][ypos+1] = curyisl[i][ypos] +
    (left[i][ypos] == 1 ? 1 : 0);
  }
}

success()
{
  int i,j;

  for(i=0; i < gridsize; i++)
    if(curyisl[i][gridsize] != numyisls[i])
      return;

  if(successcount > 0)
    bputc('\n');

  for(i=0; i < gridsize; i++) {
    for(j=0; j < gridsize; j++) {
      bputc(left[j][i] > 0 ? '*' : ' ');
      bputc(' ');
    }
    bputc('\n');
  }
  successcount++;
}

The Second Problem

Day 2: "Climbing a Mountain"

The problem remembers to the task to prepare a timeschedule for a school or similar coordination problems.

The problem will be solved using a backtracking technique.

During input of data the climbers are classified by daily consumption, so that two climbers of the same daily consumption belong to the same group. The number of such groups is kept in the variable "g". Each group is sorted by the carrying facility of the group members. By this way a tree structure is produced.

In order to solve the problem each group sends the first climber. In a recursive search using the procedure findnextclimber it is computed how far the selected climber will come and how much additional food units he needs. If the goal is not yet reached the next climber of the team is selected by calling the procedure findnextclimber indirect recursively via the procedure useclimbergroup.

Description of the program:

The algorithm is described in a semi formal manner:

          read the data;
          solve the problem.

          read the data:
            read data;
            classify the climbers;
            sort by daily consumption within the classes.

          solve the problem:
            initialize;
            for i from 1 to g use climber group (i);
            output the best result.

            use climber group:
              find next climber.

              find next climber:
                compute the number of additional food units needed;
                if the problem is totaly solved
                   then actualize the best solution;
                   else use climber group.
Protocoll of the run:
        Days to arrive to top: 4
        Number of club members: 5
        Maximal supply for climber 1: 7
        Daily consumption for climber 1: 1
        Maximal supply for climber 2: 8
        Daily consumption for climber 2: 2
        Maximal supply for climber 3: 12
        Daily consumption for climber 3: 2
        Maximal supply for climber 4: 15
        Daily consumption for climber 4: 3
        Maximal supply for climber 5: 7
        Daily consumption for climber 5: 1
        
        2 climbers needed,
        total amount of supplies is 10.
        Climber(s) 1, 5 will go.
        Climber 5 carries 7 and descends after 4 day(s)
        Climber 1 carries 3 and descends after 1 day(s)

        Plan another party (Y/N) Y

        Days to arrive to top: 2
        Number of club members: 1
        Maximal supply for climber 1: 3
        Daily consumption for climber 1: 1
        Climbing party impossible.

        Plan another party (Y/N) N

        Good bye

Program in Pascal

PROGRAM IOI92_2_climbing_party;
{ Author: Matej Ondrusek, CZ }

 USES crt;

 CONST maxp=20; { max. number of members }
       maxn=100;        { max. days }

 VAR  z:char;
      correct:boolean;
      n,p:integer;      { days, members }
      s,c:array[0..maxp] of integer;
        { max supplies, consumation }
      cg,cp:array[1..maxp] of integer;
        { cons.groups, cg pointers }
      party,bparty:array[1..maxp] of integer;
        { actual/best return's days }
      ds:array[0..maxn] of integer;
        { left supplies on days }
      dp:integer;       { days pointer }
      supplies,bsupplies:integer;
        { used/best party use supplies}
      climbers,bclimbers:integer;
        { actual/best number of clim. }
      msupplies:integer;        { minus supplies }
      g:integer;        { number of groups }
      i:integer;

 { ************************************************ }
 { ****** This procedure is the input dialog ****** }
 { ************************************************ }

 PROCEDURE readdata;
  VAR i,j,l:integer;
 BEGIN
  {$i-}
  REPEAT
   write('Days to arrive to top: '); readln(n);
   IF ioresult<>0 THEN exit;
   IF (n<1) OR (n>100) THEN
    BEGIN
     writeln(' Oops! n, is out of range (1..100) !');
     writeln(' Maybe, next time it''ll be OK ...');
     writeln;
    END;
  UNTIL (n>=1) AND (n<=100);

  REPEAT
   write('Number of club members: '); readln(p);
   IF ioresult<>0 THEN exit;
   IF (p<1) THEN
    BEGIN
     writeln(' Your club doesnt have many members.');
     writeln;
    END;
   IF (p>20) THEN
    BEGIN
     writeln('  So many climbers !');
     writeln('  It''s nice, but not allowed.');
     writeln;
    END;
  UNTIL (p>=1) AND (p<=20);

  g:=0;
  FOR i:=1 TO p DO
   BEGIN
    REPEAT
     write('Maximal supply FOR climber ',i,' : ');
     readln(s[i]);
     IF ioresult<>0 THEN exit;
     IF (s[i]<0) THEN
      BEGIN
       writeln('  I think, it cannot be negative.');
       writeln;
      END;
     IF (s[i]=0) THEN
      BEGIN
       writeln('  It''s strange, but OK.');
       writeln('  Maybe, it''s only a child ...');
      END;
    UNTIL s[i]>=0;

    REPEAT
     write('Daily consumption for climber ',i,' : ');
     readln(c[i]);
     IF ioresult<>0 THEN exit;
     IF (c[i]<1) THEN
      BEGIN
       writeln(' climber should eat at least 1.');
       writeln;
      END;
    UNTIL c[i]>=1;

    j:=1; WHILE (j<=g)AND(c[cp[j]]<>c[i]) DO j:=j+1;

    IF j>g THEN
     BEGIN
      g:=g+1;
      cp[j]:=i;
      cg[i]:=0;
     END
    ELSE IF s[i]>=s[cp[j]] THEN
     BEGIN cg[i]:=cp[j]; cp[j]:=i END
    ELSE BEGIN
     l:=cp[j];
     WHILE s[cg[l]]>s[i] DO l:=cg[l];
     cg[i]:=cg[l];
     cg[l]:=i;
    END;
   END;
  {$i+}
  correct:=true;
 END;

 { ************************************************ }
 { ***** This is only forward declaration ... ***** }
 { ************************************************ }

 PROCEDURE useclimbergroup(g:integer); FORWARD;

 { ************************************************ }
 { * This procedure finds next day to be computed * }
 { * and then it tries to use all climbers        * }
 { * on the top of groups.                        * }
 { ************************************************ }

 PROCEDURE findnextclimber;
  VAR i,dps:integer;
 BEGIN
  dps:=dp;
  WHILE (dp>0) AND (ds[dp]<=0) DO
   BEGIN
    ds[dp-1]:=ds[dp-1]+ds[dp];
    dp:=dp-1;
   END;
  IF dp=0 THEN
   BEGIN
    IF (climbers<bclimbers) OR (climbers=bclimbers)
       AND (supplies<bsupplies)
     THEN BEGIN
      bclimbers:=climbers;
      bsupplies:=supplies;
      msupplies:=ds[0];
      bparty:=party;
     END;
    END
   ELSE
    FOR i:=1 TO g DO
     IF cp[i]>0 THEN useclimbergroup(i);
   WHILE dp<dps DO
    BEGIN
     ds[dp]:=ds[dp]-ds[dp+1];
     dp:=dp+1;
    END;
 END;

 { ************************************************ }
 { * This procedure puts the best climber         * }
 { * from group g into expedition.                * }
 { ************************************************ }

 PROCEDURE useclimbergroup(g:integer);
  VAR cn:integer;
 BEGIN
  cn:=cp[g];
  IF (dp+1)*c[cn]<=s[cn] THEN
   BEGIN
    cp[g]:=cg[cn];
    party[cn]:=dp;
    climbers:=climbers+1;
    supplies:=supplies+2*dp*c[cn];
    ds[dp]:=ds[dp]-s[cn]+(dp+1)*c[cn];
    FOR i:=1 TO dp-1 DO ds[i]:=ds[i]+c[cn];

    findnextclimber;

    FOR i:=1 TO dp-1 DO ds[i]:=ds[i]-c[cn];
    ds[dp]:=ds[dp]+s[cn]-(dp+1)*c[cn];
    supplies:=supplies-2*dp*c[cn];
    climbers:=climbers-1;
    party[cn]:=0;
    cp[g]:=cn;
   END;
 END;

 { ************************************************ }
 { * This is the main one-problem solving procedure }
 { ************************************************ }

 PROCEDURE solveproblem;
  VAR i,j:integer;
 BEGIN

  bclimbers:=maxp+1;
  climbers:=0; supplies:=0;
  FOR i:=0 TO n DO ds[i]:=0;
  dp:=n;
  FOR i:=1 TO p DO party[i]:=0;

  FOR i:=1 TO g DO useclimbergroup(i);

  writeln;
  IF bclimbers=maxp+1 THEN
   BEGIN
    writeln('Climbing party impossible.');
    exit;
   END;
  j:=1; WHILE bparty[j]=0 DO j:=j+1;
  FOR i:=j+1 TO p DO
   IF (bparty[i]>0) AND (bparty[i]<bparty[j])
    THEN j:=i;
  write  (bclimbers);
  write  (' climbers needed,');
  write  (' total amount of supplies is ');
  writeln(bsupplies,'.');
  write('climber(s)');
  FOR i:=1 TO p DO
   IF bparty[i]>0 THEN write(' ',i,',');
  writeln(#8,' will go.');
  FOR i:=1 TO p DO
   IF (i<>j) AND (bparty[i]>0) THEN writeln
    ('Climber ',i,' carries ',s[i],
     ' and descends after ',bparty[i],' days');
  write  ('Climber ',j,' carries ',s[j]+msupplies);
  writeln(' and descends after ',
          bparty[j],' day(s)');
 END;

BEGIN
 s[0]:=0; c[0]:=0;
 clrscr;
 REPEAT
  correct:=false;
  REPEAT
   readdata;
   IF not correct THEN
    BEGIN
     writeln('  Only integers, please ?!?');
     writeln;
    END;
  UNTIL correct;
  solveproblem;
  REPEAT
   writeln; write('Plan another party (Y/N) ');
   readln(z);
  UNTIL z in ['y','n','Y','N'];
 UNTIL (z='n') OR (z='N');
 writeln;
 writeln('Good bye');
 writeln;
END.

Program in C

/****************************************************
  Shawn Smith   7/17/92   IOI "Climbing a Mountain"
****************************************************/

#include<stdio.h>
#include<search.h>
#include<math.h>

#define divup(a,b)
 ((int)ceil((double)(a)/(double)(b)))

struct HIKE {
  int number,carries,descends,C,S;
  /* I swapped C and S by accident */
} hike[20],best[20];

int timeabort;
int bestP,bestC,curP,curC,
    index[20],unused[20],maxindex;
int Ndays,Pclimbers;
FILE *filein;

main()
{
  int yn;

  do {
    readinfo();
    findunique();
    resetbest();
    planperson(Ndays,0,0,0,0);
    printbest();

    printf("\nPlan another party (Y/N)? ");
    while(kbhit()) getch();
    yn = getch();
  } while(yn == 'y' || yn == 'Y');
}

readinfo()
{
  int i;

  printf("\n\nDays to arrive to top: ");
  scanf("%d",&Ndays);
  if(Ndays < 1 || Ndays > 100)
    printf("Error: Invalid number of days."),exit(0);

  printf("Number of club members: ");
  scanf("%d",&Pclimbers);
  if(Pclimbers < 1 || Pclimbers > 20)
    printf("Error: Invalid number of climbers."),
    exit(0);

  for(i=0; i < Pclimbers; i++) {
    hike[i].number = i+1;

    printf("Maximal supply for climber %2d:    ",
     hike[i].number);
    scanf("%d",&hike[i].C);
    if(hike[i].C < 1)
      printf("Error: Supply must be greater 0."),
       exit(0);

    printf("Daily consumption for climber %2d: ",
     hike[i].number);
    scanf("%d",&hike[i].S);
    if(hike[i].S < 1)
      printf("Error: Consumption must greater 0."),
       exit(0);
  }
  printf("This may run for a while.
   If time is running out, hit any key.\n");
  timeabort = 0;
}

int sortorder(const void *A,const void *B)
{
  struct HIKE *a,*b;

  a = (struct HIKE *)A;
  b = (struct HIKE *)B;
  if(a->S == b->S)
    if(b->C == a->C)
      return a->number -b->number;
    else
      return b->C -a->C;
  else
    return a->S -b->S;
}

findunique()
{
  int i;

  qsort(hike,Pclimbers,sizeof(struct HIKE),
   sortorder);
  maxindex=1;
  index[0]=0;
  unused[0]=1;
  for(i=1; i < Pclimbers; i++)
    if(hike[i].S == hike[i-1].S ||
      hike[i].C <= hike[i-1].C)
      unused[maxindex-1]++;
    else {
      index[maxindex] = i;
      unused[maxindex] = 1;
      maxindex++;
    }
}

resetbest()
{
  int i;

  for(i=0; i < Pclimbers; i++) {
    best[i].number = i+1;
    best[i].carries = best[i].descends = 0;
  }
  bestP=Pclimbers+1;
}

planperson(int days,int rate,int provide,int curP,
  int curC)
{
  int i,j,needs,makeup;

  if(curP >= bestP) return;
                          /* trim the search tree */

  for(j=0; j < maxindex; j++)
    if(unused[j] > 0) {
      i = index[j];
      if(hike[i].C-hike[i].S*days >= 0) {
        hike[i].descends = days;
        needs = hike[i].S*days*2+provide;
        makeup = needs-hike[i].C;

        if(makeup > 0) {
          hike[i].carries = hike[i].C;
          index[j]++;
          unused[j]--;
          planperson(divup(makeup,hike[i].S+rate),
     hike[i].S+rate,makeup, curP+1,curC+hike[i].C);
          index[j]--;
          unused[j]++;
        }
        else {
          hike[i].carries = needs;
          checkbest(curP+1,curC+needs);
        }
        hike[i].descends = 0;
        hike[i].carries = 0;
      }
    }
}

checkbest(int curP,int curC)
{
  if(curP < bestP || (curP == bestP && curC < bestC))
  {
    bestP = curP;
    bestC = curC;
    memcpy(best,hike,Pclimbers*sizeof(struct HIKE));
  }
  printf(".");
  while(kbhit())
    bestP = 0,getch();
}

int bestorder(const void *A,const void *B)
{
  struct HIKE *a,*b;

  a = (struct HIKE *)A;
  b = (struct HIKE *)B;
  if(a->descends)
    if(b->descends)    return a->number -b->number;
    else               return -1;
  else if(b->descends) return 1;
  else                 return 0;
}

printbest()
{
  int i,first;

  qsort(best,Pclimbers,sizeof(struct HIKE),bestorder)
  ;
  for(bestP=i=0; i < Pclimbers; i++)
    if(best[i].descends)
      bestP++;

  if(bestP == 0) {
    printf("\nClimbing party impossible.\n");
    return;
  }

  printf("\n%d climber%s needed,
    total amount of supplies is %d.\n",
    bestP,bestP > 1 ? "s" : "",bestC);
  printf("Climber%s",bestP > 1 ? "s" : "");
  for(i=0,first=1; i < bestP; i++)
    printf("%s %d",first ? first=0,"" : ",",
      best[i].number);
  printf(" will go.\n");
  for(i=0; i < bestP; i++)
    printf("Climber %d carries %d and descends
      after %d day(s).\n", best[i].number,
      best[i].carries,best[i].descends);
}